diff options
Diffstat (limited to 'fs/nfs/super.c')
-rw-r--r-- | fs/nfs/super.c | 1189 |
1 files changed, 1027 insertions, 162 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index ca20d3cc2609..a2b1af89ca1a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <linux/inet.h> | 45 | #include <linux/inet.h> |
46 | #include <linux/nfs_xdr.h> | 46 | #include <linux/nfs_xdr.h> |
47 | #include <linux/magic.h> | 47 | #include <linux/magic.h> |
48 | #include <linux/parser.h> | ||
48 | 49 | ||
49 | #include <asm/system.h> | 50 | #include <asm/system.h> |
50 | #include <asm/uaccess.h> | 51 | #include <asm/uaccess.h> |
@@ -57,6 +58,167 @@ | |||
57 | 58 | ||
58 | #define NFSDBG_FACILITY NFSDBG_VFS | 59 | #define NFSDBG_FACILITY NFSDBG_VFS |
59 | 60 | ||
61 | |||
62 | struct nfs_parsed_mount_data { | ||
63 | int flags; | ||
64 | int rsize, wsize; | ||
65 | int timeo, retrans; | ||
66 | int acregmin, acregmax, | ||
67 | acdirmin, acdirmax; | ||
68 | int namlen; | ||
69 | unsigned int bsize; | ||
70 | unsigned int auth_flavor_len; | ||
71 | rpc_authflavor_t auth_flavors[1]; | ||
72 | char *client_address; | ||
73 | |||
74 | struct { | ||
75 | struct sockaddr_in address; | ||
76 | unsigned int program; | ||
77 | unsigned int version; | ||
78 | unsigned short port; | ||
79 | int protocol; | ||
80 | } mount_server; | ||
81 | |||
82 | struct { | ||
83 | struct sockaddr_in address; | ||
84 | char *hostname; | ||
85 | char *export_path; | ||
86 | unsigned int program; | ||
87 | int protocol; | ||
88 | } nfs_server; | ||
89 | }; | ||
90 | |||
91 | enum { | ||
92 | /* Mount options that take no arguments */ | ||
93 | Opt_soft, Opt_hard, | ||
94 | Opt_intr, Opt_nointr, | ||
95 | Opt_posix, Opt_noposix, | ||
96 | Opt_cto, Opt_nocto, | ||
97 | Opt_ac, Opt_noac, | ||
98 | Opt_lock, Opt_nolock, | ||
99 | Opt_v2, Opt_v3, | ||
100 | Opt_udp, Opt_tcp, | ||
101 | Opt_acl, Opt_noacl, | ||
102 | Opt_rdirplus, Opt_nordirplus, | ||
103 | Opt_sharecache, Opt_nosharecache, | ||
104 | |||
105 | /* Mount options that take integer arguments */ | ||
106 | Opt_port, | ||
107 | Opt_rsize, Opt_wsize, Opt_bsize, | ||
108 | Opt_timeo, Opt_retrans, | ||
109 | Opt_acregmin, Opt_acregmax, | ||
110 | Opt_acdirmin, Opt_acdirmax, | ||
111 | Opt_actimeo, | ||
112 | Opt_namelen, | ||
113 | Opt_mountport, | ||
114 | Opt_mountprog, Opt_mountvers, | ||
115 | Opt_nfsprog, Opt_nfsvers, | ||
116 | |||
117 | /* Mount options that take string arguments */ | ||
118 | Opt_sec, Opt_proto, Opt_mountproto, | ||
119 | Opt_addr, Opt_mounthost, Opt_clientaddr, | ||
120 | |||
121 | /* Mount options that are ignored */ | ||
122 | Opt_userspace, Opt_deprecated, | ||
123 | |||
124 | Opt_err | ||
125 | }; | ||
126 | |||
127 | static match_table_t nfs_mount_option_tokens = { | ||
128 | { Opt_userspace, "bg" }, | ||
129 | { Opt_userspace, "fg" }, | ||
130 | { Opt_soft, "soft" }, | ||
131 | { Opt_hard, "hard" }, | ||
132 | { Opt_intr, "intr" }, | ||
133 | { Opt_nointr, "nointr" }, | ||
134 | { Opt_posix, "posix" }, | ||
135 | { Opt_noposix, "noposix" }, | ||
136 | { Opt_cto, "cto" }, | ||
137 | { Opt_nocto, "nocto" }, | ||
138 | { Opt_ac, "ac" }, | ||
139 | { Opt_noac, "noac" }, | ||
140 | { Opt_lock, "lock" }, | ||
141 | { Opt_nolock, "nolock" }, | ||
142 | { Opt_v2, "v2" }, | ||
143 | { Opt_v3, "v3" }, | ||
144 | { Opt_udp, "udp" }, | ||
145 | { Opt_tcp, "tcp" }, | ||
146 | { Opt_acl, "acl" }, | ||
147 | { Opt_noacl, "noacl" }, | ||
148 | { Opt_rdirplus, "rdirplus" }, | ||
149 | { Opt_nordirplus, "nordirplus" }, | ||
150 | { Opt_sharecache, "sharecache" }, | ||
151 | { Opt_nosharecache, "nosharecache" }, | ||
152 | |||
153 | { Opt_port, "port=%u" }, | ||
154 | { Opt_rsize, "rsize=%u" }, | ||
155 | { Opt_wsize, "wsize=%u" }, | ||
156 | { Opt_bsize, "bsize=%u" }, | ||
157 | { Opt_timeo, "timeo=%u" }, | ||
158 | { Opt_retrans, "retrans=%u" }, | ||
159 | { Opt_acregmin, "acregmin=%u" }, | ||
160 | { Opt_acregmax, "acregmax=%u" }, | ||
161 | { Opt_acdirmin, "acdirmin=%u" }, | ||
162 | { Opt_acdirmax, "acdirmax=%u" }, | ||
163 | { Opt_actimeo, "actimeo=%u" }, | ||
164 | { Opt_userspace, "retry=%u" }, | ||
165 | { Opt_namelen, "namlen=%u" }, | ||
166 | { Opt_mountport, "mountport=%u" }, | ||
167 | { Opt_mountprog, "mountprog=%u" }, | ||
168 | { Opt_mountvers, "mountvers=%u" }, | ||
169 | { Opt_nfsprog, "nfsprog=%u" }, | ||
170 | { Opt_nfsvers, "nfsvers=%u" }, | ||
171 | { Opt_nfsvers, "vers=%u" }, | ||
172 | |||
173 | { Opt_sec, "sec=%s" }, | ||
174 | { Opt_proto, "proto=%s" }, | ||
175 | { Opt_mountproto, "mountproto=%s" }, | ||
176 | { Opt_addr, "addr=%s" }, | ||
177 | { Opt_clientaddr, "clientaddr=%s" }, | ||
178 | { Opt_mounthost, "mounthost=%s" }, | ||
179 | |||
180 | { Opt_err, NULL } | ||
181 | }; | ||
182 | |||
183 | enum { | ||
184 | Opt_xprt_udp, Opt_xprt_tcp, | ||
185 | |||
186 | Opt_xprt_err | ||
187 | }; | ||
188 | |||
189 | static match_table_t nfs_xprt_protocol_tokens = { | ||
190 | { Opt_xprt_udp, "udp" }, | ||
191 | { Opt_xprt_tcp, "tcp" }, | ||
192 | |||
193 | { Opt_xprt_err, NULL } | ||
194 | }; | ||
195 | |||
196 | enum { | ||
197 | Opt_sec_none, Opt_sec_sys, | ||
198 | Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p, | ||
199 | Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp, | ||
200 | Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp, | ||
201 | |||
202 | Opt_sec_err | ||
203 | }; | ||
204 | |||
205 | static match_table_t nfs_secflavor_tokens = { | ||
206 | { Opt_sec_none, "none" }, | ||
207 | { Opt_sec_none, "null" }, | ||
208 | { Opt_sec_sys, "sys" }, | ||
209 | |||
210 | { Opt_sec_krb5, "krb5" }, | ||
211 | { Opt_sec_krb5i, "krb5i" }, | ||
212 | { Opt_sec_krb5p, "krb5p" }, | ||
213 | |||
214 | { Opt_sec_lkey, "lkey" }, | ||
215 | { Opt_sec_lkeyi, "lkeyi" }, | ||
216 | { Opt_sec_lkeyp, "lkeyp" }, | ||
217 | |||
218 | { Opt_sec_err, NULL } | ||
219 | }; | ||
220 | |||
221 | |||
60 | static void nfs_umount_begin(struct vfsmount *, int); | 222 | static void nfs_umount_begin(struct vfsmount *, int); |
61 | static int nfs_statfs(struct dentry *, struct kstatfs *); | 223 | static int nfs_statfs(struct dentry *, struct kstatfs *); |
62 | static int nfs_show_options(struct seq_file *, struct vfsmount *); | 224 | static int nfs_show_options(struct seq_file *, struct vfsmount *); |
@@ -263,11 +425,11 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour) | |||
263 | { RPC_AUTH_GSS_SPKM, "spkm" }, | 425 | { RPC_AUTH_GSS_SPKM, "spkm" }, |
264 | { RPC_AUTH_GSS_SPKMI, "spkmi" }, | 426 | { RPC_AUTH_GSS_SPKMI, "spkmi" }, |
265 | { RPC_AUTH_GSS_SPKMP, "spkmp" }, | 427 | { RPC_AUTH_GSS_SPKMP, "spkmp" }, |
266 | { -1, "unknown" } | 428 | { UINT_MAX, "unknown" } |
267 | }; | 429 | }; |
268 | int i; | 430 | int i; |
269 | 431 | ||
270 | for (i=0; sec_flavours[i].flavour != -1; i++) { | 432 | for (i = 0; sec_flavours[i].flavour != UINT_MAX; i++) { |
271 | if (sec_flavours[i].flavour == flavour) | 433 | if (sec_flavours[i].flavour == flavour) |
272 | break; | 434 | break; |
273 | } | 435 | } |
@@ -291,6 +453,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, | |||
291 | { NFS_MOUNT_NONLM, ",nolock", "" }, | 453 | { NFS_MOUNT_NONLM, ",nolock", "" }, |
292 | { NFS_MOUNT_NOACL, ",noacl", "" }, | 454 | { NFS_MOUNT_NOACL, ",noacl", "" }, |
293 | { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" }, | 455 | { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" }, |
456 | { NFS_MOUNT_UNSHARED, ",nosharecache", ""}, | ||
294 | { 0, NULL, NULL } | 457 | { 0, NULL, NULL } |
295 | }; | 458 | }; |
296 | const struct proc_nfs_info *nfs_infop; | 459 | const struct proc_nfs_info *nfs_infop; |
@@ -430,87 +593,641 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) | |||
430 | */ | 593 | */ |
431 | static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) | 594 | static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) |
432 | { | 595 | { |
596 | struct nfs_server *server = NFS_SB(vfsmnt->mnt_sb); | ||
597 | struct rpc_clnt *rpc; | ||
598 | |||
433 | shrink_submounts(vfsmnt, &nfs_automount_list); | 599 | shrink_submounts(vfsmnt, &nfs_automount_list); |
600 | |||
601 | if (!(flags & MNT_FORCE)) | ||
602 | return; | ||
603 | /* -EIO all pending I/O */ | ||
604 | rpc = server->client_acl; | ||
605 | if (!IS_ERR(rpc)) | ||
606 | rpc_killall_tasks(rpc); | ||
607 | rpc = server->client; | ||
608 | if (!IS_ERR(rpc)) | ||
609 | rpc_killall_tasks(rpc); | ||
434 | } | 610 | } |
435 | 611 | ||
436 | /* | 612 | /* |
437 | * Validate the NFS2/NFS3 mount data | 613 | * Sanity-check a server address provided by the mount command |
438 | * - fills in the mount root filehandle | ||
439 | */ | 614 | */ |
440 | static int nfs_validate_mount_data(struct nfs_mount_data *data, | 615 | static int nfs_verify_server_address(struct sockaddr *addr) |
441 | struct nfs_fh *mntfh) | ||
442 | { | 616 | { |
443 | if (data == NULL) { | 617 | switch (addr->sa_family) { |
444 | dprintk("%s: missing data argument\n", __FUNCTION__); | 618 | case AF_INET: { |
445 | return -EINVAL; | 619 | struct sockaddr_in *sa = (struct sockaddr_in *) addr; |
620 | if (sa->sin_addr.s_addr != INADDR_ANY) | ||
621 | return 1; | ||
622 | break; | ||
623 | } | ||
446 | } | 624 | } |
447 | 625 | ||
448 | if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { | 626 | return 0; |
449 | dprintk("%s: bad mount version\n", __FUNCTION__); | 627 | } |
450 | return -EINVAL; | 628 | |
629 | /* | ||
630 | * Error-check and convert a string of mount options from user space into | ||
631 | * a data structure | ||
632 | */ | ||
633 | static int nfs_parse_mount_options(char *raw, | ||
634 | struct nfs_parsed_mount_data *mnt) | ||
635 | { | ||
636 | char *p, *string; | ||
637 | |||
638 | if (!raw) { | ||
639 | dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); | ||
640 | return 1; | ||
451 | } | 641 | } |
642 | dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw); | ||
452 | 643 | ||
453 | switch (data->version) { | 644 | while ((p = strsep(&raw, ",")) != NULL) { |
454 | case 1: | 645 | substring_t args[MAX_OPT_ARGS]; |
455 | data->namlen = 0; | 646 | int option, token; |
456 | case 2: | 647 | |
457 | data->bsize = 0; | 648 | if (!*p) |
458 | case 3: | 649 | continue; |
459 | if (data->flags & NFS_MOUNT_VER3) { | 650 | |
460 | dprintk("%s: mount structure version %d does not support NFSv3\n", | 651 | dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", p); |
461 | __FUNCTION__, | 652 | |
462 | data->version); | 653 | token = match_token(p, nfs_mount_option_tokens, args); |
463 | return -EINVAL; | 654 | switch (token) { |
655 | case Opt_soft: | ||
656 | mnt->flags |= NFS_MOUNT_SOFT; | ||
657 | break; | ||
658 | case Opt_hard: | ||
659 | mnt->flags &= ~NFS_MOUNT_SOFT; | ||
660 | break; | ||
661 | case Opt_intr: | ||
662 | mnt->flags |= NFS_MOUNT_INTR; | ||
663 | break; | ||
664 | case Opt_nointr: | ||
665 | mnt->flags &= ~NFS_MOUNT_INTR; | ||
666 | break; | ||
667 | case Opt_posix: | ||
668 | mnt->flags |= NFS_MOUNT_POSIX; | ||
669 | break; | ||
670 | case Opt_noposix: | ||
671 | mnt->flags &= ~NFS_MOUNT_POSIX; | ||
672 | break; | ||
673 | case Opt_cto: | ||
674 | mnt->flags &= ~NFS_MOUNT_NOCTO; | ||
675 | break; | ||
676 | case Opt_nocto: | ||
677 | mnt->flags |= NFS_MOUNT_NOCTO; | ||
678 | break; | ||
679 | case Opt_ac: | ||
680 | mnt->flags &= ~NFS_MOUNT_NOAC; | ||
681 | break; | ||
682 | case Opt_noac: | ||
683 | mnt->flags |= NFS_MOUNT_NOAC; | ||
684 | break; | ||
685 | case Opt_lock: | ||
686 | mnt->flags &= ~NFS_MOUNT_NONLM; | ||
687 | break; | ||
688 | case Opt_nolock: | ||
689 | mnt->flags |= NFS_MOUNT_NONLM; | ||
690 | break; | ||
691 | case Opt_v2: | ||
692 | mnt->flags &= ~NFS_MOUNT_VER3; | ||
693 | break; | ||
694 | case Opt_v3: | ||
695 | mnt->flags |= NFS_MOUNT_VER3; | ||
696 | break; | ||
697 | case Opt_udp: | ||
698 | mnt->flags &= ~NFS_MOUNT_TCP; | ||
699 | mnt->nfs_server.protocol = IPPROTO_UDP; | ||
700 | mnt->timeo = 7; | ||
701 | mnt->retrans = 5; | ||
702 | break; | ||
703 | case Opt_tcp: | ||
704 | mnt->flags |= NFS_MOUNT_TCP; | ||
705 | mnt->nfs_server.protocol = IPPROTO_TCP; | ||
706 | mnt->timeo = 600; | ||
707 | mnt->retrans = 2; | ||
708 | break; | ||
709 | case Opt_acl: | ||
710 | mnt->flags &= ~NFS_MOUNT_NOACL; | ||
711 | break; | ||
712 | case Opt_noacl: | ||
713 | mnt->flags |= NFS_MOUNT_NOACL; | ||
714 | break; | ||
715 | case Opt_rdirplus: | ||
716 | mnt->flags &= ~NFS_MOUNT_NORDIRPLUS; | ||
717 | break; | ||
718 | case Opt_nordirplus: | ||
719 | mnt->flags |= NFS_MOUNT_NORDIRPLUS; | ||
720 | break; | ||
721 | case Opt_sharecache: | ||
722 | mnt->flags &= ~NFS_MOUNT_UNSHARED; | ||
723 | break; | ||
724 | case Opt_nosharecache: | ||
725 | mnt->flags |= NFS_MOUNT_UNSHARED; | ||
726 | break; | ||
727 | |||
728 | case Opt_port: | ||
729 | if (match_int(args, &option)) | ||
730 | return 0; | ||
731 | if (option < 0 || option > 65535) | ||
732 | return 0; | ||
733 | mnt->nfs_server.address.sin_port = htonl(option); | ||
734 | break; | ||
735 | case Opt_rsize: | ||
736 | if (match_int(args, &mnt->rsize)) | ||
737 | return 0; | ||
738 | break; | ||
739 | case Opt_wsize: | ||
740 | if (match_int(args, &mnt->wsize)) | ||
741 | return 0; | ||
742 | break; | ||
743 | case Opt_bsize: | ||
744 | if (match_int(args, &option)) | ||
745 | return 0; | ||
746 | if (option < 0) | ||
747 | return 0; | ||
748 | mnt->bsize = option; | ||
749 | break; | ||
750 | case Opt_timeo: | ||
751 | if (match_int(args, &mnt->timeo)) | ||
752 | return 0; | ||
753 | break; | ||
754 | case Opt_retrans: | ||
755 | if (match_int(args, &mnt->retrans)) | ||
756 | return 0; | ||
757 | break; | ||
758 | case Opt_acregmin: | ||
759 | if (match_int(args, &mnt->acregmin)) | ||
760 | return 0; | ||
761 | break; | ||
762 | case Opt_acregmax: | ||
763 | if (match_int(args, &mnt->acregmax)) | ||
764 | return 0; | ||
765 | break; | ||
766 | case Opt_acdirmin: | ||
767 | if (match_int(args, &mnt->acdirmin)) | ||
768 | return 0; | ||
769 | break; | ||
770 | case Opt_acdirmax: | ||
771 | if (match_int(args, &mnt->acdirmax)) | ||
772 | return 0; | ||
773 | break; | ||
774 | case Opt_actimeo: | ||
775 | if (match_int(args, &option)) | ||
776 | return 0; | ||
777 | if (option < 0) | ||
778 | return 0; | ||
779 | mnt->acregmin = | ||
780 | mnt->acregmax = | ||
781 | mnt->acdirmin = | ||
782 | mnt->acdirmax = option; | ||
783 | break; | ||
784 | case Opt_namelen: | ||
785 | if (match_int(args, &mnt->namlen)) | ||
786 | return 0; | ||
787 | break; | ||
788 | case Opt_mountport: | ||
789 | if (match_int(args, &option)) | ||
790 | return 0; | ||
791 | if (option < 0 || option > 65535) | ||
792 | return 0; | ||
793 | mnt->mount_server.port = option; | ||
794 | break; | ||
795 | case Opt_mountprog: | ||
796 | if (match_int(args, &option)) | ||
797 | return 0; | ||
798 | if (option < 0) | ||
799 | return 0; | ||
800 | mnt->mount_server.program = option; | ||
801 | break; | ||
802 | case Opt_mountvers: | ||
803 | if (match_int(args, &option)) | ||
804 | return 0; | ||
805 | if (option < 0) | ||
806 | return 0; | ||
807 | mnt->mount_server.version = option; | ||
808 | break; | ||
809 | case Opt_nfsprog: | ||
810 | if (match_int(args, &option)) | ||
811 | return 0; | ||
812 | if (option < 0) | ||
813 | return 0; | ||
814 | mnt->nfs_server.program = option; | ||
815 | break; | ||
816 | case Opt_nfsvers: | ||
817 | if (match_int(args, &option)) | ||
818 | return 0; | ||
819 | switch (option) { | ||
820 | case 2: | ||
821 | mnt->flags &= ~NFS_MOUNT_VER3; | ||
822 | break; | ||
823 | case 3: | ||
824 | mnt->flags |= NFS_MOUNT_VER3; | ||
825 | break; | ||
826 | default: | ||
827 | goto out_unrec_vers; | ||
464 | } | 828 | } |
465 | data->root.size = NFS2_FHSIZE; | 829 | break; |
466 | memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); | 830 | |
467 | case 4: | 831 | case Opt_sec: |
468 | if (data->flags & NFS_MOUNT_SECFLAVOUR) { | 832 | string = match_strdup(args); |
469 | dprintk("%s: mount structure version %d does not support strong security\n", | 833 | if (string == NULL) |
470 | __FUNCTION__, | 834 | goto out_nomem; |
471 | data->version); | 835 | token = match_token(string, nfs_secflavor_tokens, args); |
472 | return -EINVAL; | 836 | kfree(string); |
837 | |||
838 | /* | ||
839 | * The flags setting is for v2/v3. The flavor_len | ||
840 | * setting is for v4. v2/v3 also need to know the | ||
841 | * difference between NULL and UNIX. | ||
842 | */ | ||
843 | switch (token) { | ||
844 | case Opt_sec_none: | ||
845 | mnt->flags &= ~NFS_MOUNT_SECFLAVOUR; | ||
846 | mnt->auth_flavor_len = 0; | ||
847 | mnt->auth_flavors[0] = RPC_AUTH_NULL; | ||
848 | break; | ||
849 | case Opt_sec_sys: | ||
850 | mnt->flags &= ~NFS_MOUNT_SECFLAVOUR; | ||
851 | mnt->auth_flavor_len = 0; | ||
852 | mnt->auth_flavors[0] = RPC_AUTH_UNIX; | ||
853 | break; | ||
854 | case Opt_sec_krb5: | ||
855 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
856 | mnt->auth_flavor_len = 1; | ||
857 | mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5; | ||
858 | break; | ||
859 | case Opt_sec_krb5i: | ||
860 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
861 | mnt->auth_flavor_len = 1; | ||
862 | mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I; | ||
863 | break; | ||
864 | case Opt_sec_krb5p: | ||
865 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
866 | mnt->auth_flavor_len = 1; | ||
867 | mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P; | ||
868 | break; | ||
869 | case Opt_sec_lkey: | ||
870 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
871 | mnt->auth_flavor_len = 1; | ||
872 | mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY; | ||
873 | break; | ||
874 | case Opt_sec_lkeyi: | ||
875 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
876 | mnt->auth_flavor_len = 1; | ||
877 | mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI; | ||
878 | break; | ||
879 | case Opt_sec_lkeyp: | ||
880 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
881 | mnt->auth_flavor_len = 1; | ||
882 | mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP; | ||
883 | break; | ||
884 | case Opt_sec_spkm: | ||
885 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
886 | mnt->auth_flavor_len = 1; | ||
887 | mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM; | ||
888 | break; | ||
889 | case Opt_sec_spkmi: | ||
890 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
891 | mnt->auth_flavor_len = 1; | ||
892 | mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI; | ||
893 | break; | ||
894 | case Opt_sec_spkmp: | ||
895 | mnt->flags |= NFS_MOUNT_SECFLAVOUR; | ||
896 | mnt->auth_flavor_len = 1; | ||
897 | mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP; | ||
898 | break; | ||
899 | default: | ||
900 | goto out_unrec_sec; | ||
473 | } | 901 | } |
474 | case 5: | 902 | break; |
475 | memset(data->context, 0, sizeof(data->context)); | 903 | case Opt_proto: |
476 | } | 904 | string = match_strdup(args); |
905 | if (string == NULL) | ||
906 | goto out_nomem; | ||
907 | token = match_token(string, | ||
908 | nfs_xprt_protocol_tokens, args); | ||
909 | kfree(string); | ||
910 | |||
911 | switch (token) { | ||
912 | case Opt_udp: | ||
913 | mnt->flags &= ~NFS_MOUNT_TCP; | ||
914 | mnt->nfs_server.protocol = IPPROTO_UDP; | ||
915 | mnt->timeo = 7; | ||
916 | mnt->retrans = 5; | ||
917 | break; | ||
918 | case Opt_tcp: | ||
919 | mnt->flags |= NFS_MOUNT_TCP; | ||
920 | mnt->nfs_server.protocol = IPPROTO_TCP; | ||
921 | mnt->timeo = 600; | ||
922 | mnt->retrans = 2; | ||
923 | break; | ||
924 | default: | ||
925 | goto out_unrec_xprt; | ||
926 | } | ||
927 | break; | ||
928 | case Opt_mountproto: | ||
929 | string = match_strdup(args); | ||
930 | if (string == NULL) | ||
931 | goto out_nomem; | ||
932 | token = match_token(string, | ||
933 | nfs_xprt_protocol_tokens, args); | ||
934 | kfree(string); | ||
935 | |||
936 | switch (token) { | ||
937 | case Opt_udp: | ||
938 | mnt->mount_server.protocol = IPPROTO_UDP; | ||
939 | break; | ||
940 | case Opt_tcp: | ||
941 | mnt->mount_server.protocol = IPPROTO_TCP; | ||
942 | break; | ||
943 | default: | ||
944 | goto out_unrec_xprt; | ||
945 | } | ||
946 | break; | ||
947 | case Opt_addr: | ||
948 | string = match_strdup(args); | ||
949 | if (string == NULL) | ||
950 | goto out_nomem; | ||
951 | mnt->nfs_server.address.sin_family = AF_INET; | ||
952 | mnt->nfs_server.address.sin_addr.s_addr = | ||
953 | in_aton(string); | ||
954 | kfree(string); | ||
955 | break; | ||
956 | case Opt_clientaddr: | ||
957 | string = match_strdup(args); | ||
958 | if (string == NULL) | ||
959 | goto out_nomem; | ||
960 | mnt->client_address = string; | ||
961 | break; | ||
962 | case Opt_mounthost: | ||
963 | string = match_strdup(args); | ||
964 | if (string == NULL) | ||
965 | goto out_nomem; | ||
966 | mnt->mount_server.address.sin_family = AF_INET; | ||
967 | mnt->mount_server.address.sin_addr.s_addr = | ||
968 | in_aton(string); | ||
969 | kfree(string); | ||
970 | break; | ||
477 | 971 | ||
478 | /* Set the pseudoflavor */ | 972 | case Opt_userspace: |
479 | if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) | 973 | case Opt_deprecated: |
480 | data->pseudoflavor = RPC_AUTH_UNIX; | 974 | break; |
481 | 975 | ||
482 | #ifndef CONFIG_NFS_V3 | 976 | default: |
483 | /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ | 977 | goto out_unknown; |
484 | if (data->flags & NFS_MOUNT_VER3) { | 978 | } |
485 | dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); | ||
486 | return -EPROTONOSUPPORT; | ||
487 | } | 979 | } |
488 | #endif /* CONFIG_NFS_V3 */ | ||
489 | 980 | ||
490 | /* We now require that the mount process passes the remote address */ | 981 | return 1; |
491 | if (data->addr.sin_addr.s_addr == INADDR_ANY) { | 982 | |
492 | dprintk("%s: mount program didn't pass remote address!\n", | 983 | out_nomem: |
493 | __FUNCTION__); | 984 | printk(KERN_INFO "NFS: not enough memory to parse option\n"); |
494 | return -EINVAL; | 985 | return 0; |
986 | |||
987 | out_unrec_vers: | ||
988 | printk(KERN_INFO "NFS: unrecognized NFS version number\n"); | ||
989 | return 0; | ||
990 | |||
991 | out_unrec_xprt: | ||
992 | printk(KERN_INFO "NFS: unrecognized transport protocol\n"); | ||
993 | return 0; | ||
994 | |||
995 | out_unrec_sec: | ||
996 | printk(KERN_INFO "NFS: unrecognized security flavor\n"); | ||
997 | return 0; | ||
998 | |||
999 | out_unknown: | ||
1000 | printk(KERN_INFO "NFS: unknown mount option: %s\n", p); | ||
1001 | return 0; | ||
1002 | } | ||
1003 | |||
1004 | /* | ||
1005 | * Use the remote server's MOUNT service to request the NFS file handle | ||
1006 | * corresponding to the provided path. | ||
1007 | */ | ||
1008 | static int nfs_try_mount(struct nfs_parsed_mount_data *args, | ||
1009 | struct nfs_fh *root_fh) | ||
1010 | { | ||
1011 | struct sockaddr_in sin; | ||
1012 | int status; | ||
1013 | |||
1014 | if (args->mount_server.version == 0) { | ||
1015 | if (args->flags & NFS_MOUNT_VER3) | ||
1016 | args->mount_server.version = NFS_MNT3_VERSION; | ||
1017 | else | ||
1018 | args->mount_server.version = NFS_MNT_VERSION; | ||
495 | } | 1019 | } |
496 | 1020 | ||
497 | /* Prepare the root filehandle */ | 1021 | /* |
498 | if (data->flags & NFS_MOUNT_VER3) | 1022 | * Construct the mount server's address. |
499 | mntfh->size = data->root.size; | 1023 | */ |
1024 | if (args->mount_server.address.sin_addr.s_addr != INADDR_ANY) | ||
1025 | sin = args->mount_server.address; | ||
500 | else | 1026 | else |
501 | mntfh->size = NFS2_FHSIZE; | 1027 | sin = args->nfs_server.address; |
1028 | if (args->mount_server.port == 0) { | ||
1029 | status = rpcb_getport_sync(&sin, | ||
1030 | args->mount_server.program, | ||
1031 | args->mount_server.version, | ||
1032 | args->mount_server.protocol); | ||
1033 | if (status < 0) | ||
1034 | goto out_err; | ||
1035 | sin.sin_port = htons(status); | ||
1036 | } else | ||
1037 | sin.sin_port = htons(args->mount_server.port); | ||
1038 | |||
1039 | /* | ||
1040 | * Now ask the mount server to map our export path | ||
1041 | * to a file handle. | ||
1042 | */ | ||
1043 | status = nfs_mount((struct sockaddr *) &sin, | ||
1044 | sizeof(sin), | ||
1045 | args->nfs_server.hostname, | ||
1046 | args->nfs_server.export_path, | ||
1047 | args->mount_server.version, | ||
1048 | args->mount_server.protocol, | ||
1049 | root_fh); | ||
1050 | if (status < 0) | ||
1051 | goto out_err; | ||
1052 | |||
1053 | return status; | ||
502 | 1054 | ||
503 | if (mntfh->size > sizeof(mntfh->data)) { | 1055 | out_err: |
504 | dprintk("%s: invalid root filehandle\n", __FUNCTION__); | 1056 | dfprintk(MOUNT, "NFS: unable to contact server on host " |
505 | return -EINVAL; | 1057 | NIPQUAD_FMT "\n", NIPQUAD(sin.sin_addr.s_addr)); |
1058 | return status; | ||
1059 | } | ||
1060 | |||
1061 | /* | ||
1062 | * Validate the NFS2/NFS3 mount data | ||
1063 | * - fills in the mount root filehandle | ||
1064 | * | ||
1065 | * For option strings, user space handles the following behaviors: | ||
1066 | * | ||
1067 | * + DNS: mapping server host name to IP address ("addr=" option) | ||
1068 | * | ||
1069 | * + failure mode: how to behave if a mount request can't be handled | ||
1070 | * immediately ("fg/bg" option) | ||
1071 | * | ||
1072 | * + retry: how often to retry a mount request ("retry=" option) | ||
1073 | * | ||
1074 | * + breaking back: trying proto=udp after proto=tcp, v2 after v3, | ||
1075 | * mountproto=tcp after mountproto=udp, and so on | ||
1076 | * | ||
1077 | * XXX: as far as I can tell, changing the NFS program number is not | ||
1078 | * supported in the NFS client. | ||
1079 | */ | ||
1080 | static int nfs_validate_mount_data(struct nfs_mount_data **options, | ||
1081 | struct nfs_fh *mntfh, | ||
1082 | const char *dev_name) | ||
1083 | { | ||
1084 | struct nfs_mount_data *data = *options; | ||
1085 | |||
1086 | if (data == NULL) | ||
1087 | goto out_no_data; | ||
1088 | |||
1089 | switch (data->version) { | ||
1090 | case 1: | ||
1091 | data->namlen = 0; | ||
1092 | case 2: | ||
1093 | data->bsize = 0; | ||
1094 | case 3: | ||
1095 | if (data->flags & NFS_MOUNT_VER3) | ||
1096 | goto out_no_v3; | ||
1097 | data->root.size = NFS2_FHSIZE; | ||
1098 | memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); | ||
1099 | case 4: | ||
1100 | if (data->flags & NFS_MOUNT_SECFLAVOUR) | ||
1101 | goto out_no_sec; | ||
1102 | case 5: | ||
1103 | memset(data->context, 0, sizeof(data->context)); | ||
1104 | case 6: | ||
1105 | if (data->flags & NFS_MOUNT_VER3) | ||
1106 | mntfh->size = data->root.size; | ||
1107 | else | ||
1108 | mntfh->size = NFS2_FHSIZE; | ||
1109 | |||
1110 | if (mntfh->size > sizeof(mntfh->data)) | ||
1111 | goto out_invalid_fh; | ||
1112 | |||
1113 | memcpy(mntfh->data, data->root.data, mntfh->size); | ||
1114 | if (mntfh->size < sizeof(mntfh->data)) | ||
1115 | memset(mntfh->data + mntfh->size, 0, | ||
1116 | sizeof(mntfh->data) - mntfh->size); | ||
1117 | break; | ||
1118 | default: { | ||
1119 | unsigned int len; | ||
1120 | char *c; | ||
1121 | int status; | ||
1122 | struct nfs_parsed_mount_data args = { | ||
1123 | .flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP), | ||
1124 | .rsize = NFS_MAX_FILE_IO_SIZE, | ||
1125 | .wsize = NFS_MAX_FILE_IO_SIZE, | ||
1126 | .timeo = 600, | ||
1127 | .retrans = 2, | ||
1128 | .acregmin = 3, | ||
1129 | .acregmax = 60, | ||
1130 | .acdirmin = 30, | ||
1131 | .acdirmax = 60, | ||
1132 | .mount_server.protocol = IPPROTO_UDP, | ||
1133 | .mount_server.program = NFS_MNT_PROGRAM, | ||
1134 | .nfs_server.protocol = IPPROTO_TCP, | ||
1135 | .nfs_server.program = NFS_PROGRAM, | ||
1136 | }; | ||
1137 | |||
1138 | if (nfs_parse_mount_options((char *) *options, &args) == 0) | ||
1139 | return -EINVAL; | ||
1140 | |||
1141 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
1142 | if (data == NULL) | ||
1143 | return -ENOMEM; | ||
1144 | |||
1145 | /* | ||
1146 | * NB: after this point, caller will free "data" | ||
1147 | * if we return an error | ||
1148 | */ | ||
1149 | *options = data; | ||
1150 | |||
1151 | c = strchr(dev_name, ':'); | ||
1152 | if (c == NULL) | ||
1153 | return -EINVAL; | ||
1154 | len = c - dev_name - 1; | ||
1155 | if (len > sizeof(data->hostname)) | ||
1156 | return -EINVAL; | ||
1157 | strncpy(data->hostname, dev_name, len); | ||
1158 | args.nfs_server.hostname = data->hostname; | ||
1159 | |||
1160 | c++; | ||
1161 | if (strlen(c) > NFS_MAXPATHLEN) | ||
1162 | return -EINVAL; | ||
1163 | args.nfs_server.export_path = c; | ||
1164 | |||
1165 | status = nfs_try_mount(&args, mntfh); | ||
1166 | if (status) | ||
1167 | return -EINVAL; | ||
1168 | |||
1169 | /* | ||
1170 | * Translate to nfs_mount_data, which nfs_fill_super | ||
1171 | * can deal with. | ||
1172 | */ | ||
1173 | data->version = 6; | ||
1174 | data->flags = args.flags; | ||
1175 | data->rsize = args.rsize; | ||
1176 | data->wsize = args.wsize; | ||
1177 | data->timeo = args.timeo; | ||
1178 | data->retrans = args.retrans; | ||
1179 | data->acregmin = args.acregmin; | ||
1180 | data->acregmax = args.acregmax; | ||
1181 | data->acdirmin = args.acdirmin; | ||
1182 | data->acdirmax = args.acdirmax; | ||
1183 | data->addr = args.nfs_server.address; | ||
1184 | data->namlen = args.namlen; | ||
1185 | data->bsize = args.bsize; | ||
1186 | data->pseudoflavor = args.auth_flavors[0]; | ||
1187 | |||
1188 | break; | ||
1189 | } | ||
506 | } | 1190 | } |
507 | 1191 | ||
508 | memcpy(mntfh->data, data->root.data, mntfh->size); | 1192 | if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) |
509 | if (mntfh->size < sizeof(mntfh->data)) | 1193 | data->pseudoflavor = RPC_AUTH_UNIX; |
510 | memset(mntfh->data + mntfh->size, 0, | 1194 | |
511 | sizeof(mntfh->data) - mntfh->size); | 1195 | #ifndef CONFIG_NFS_V3 |
1196 | if (data->flags & NFS_MOUNT_VER3) | ||
1197 | goto out_v3_not_compiled; | ||
1198 | #endif /* !CONFIG_NFS_V3 */ | ||
1199 | |||
1200 | if (!nfs_verify_server_address((struct sockaddr *) &data->addr)) | ||
1201 | goto out_no_address; | ||
512 | 1202 | ||
513 | return 0; | 1203 | return 0; |
1204 | |||
1205 | out_no_data: | ||
1206 | dfprintk(MOUNT, "NFS: mount program didn't pass any mount data\n"); | ||
1207 | return -EINVAL; | ||
1208 | |||
1209 | out_no_v3: | ||
1210 | dfprintk(MOUNT, "NFS: nfs_mount_data version %d does not support v3\n", | ||
1211 | data->version); | ||
1212 | return -EINVAL; | ||
1213 | |||
1214 | out_no_sec: | ||
1215 | dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n"); | ||
1216 | return -EINVAL; | ||
1217 | |||
1218 | #ifndef CONFIG_NFS_V3 | ||
1219 | out_v3_not_compiled: | ||
1220 | dfprintk(MOUNT, "NFS: NFSv3 is not compiled into kernel\n"); | ||
1221 | return -EPROTONOSUPPORT; | ||
1222 | #endif /* !CONFIG_NFS_V3 */ | ||
1223 | |||
1224 | out_no_address: | ||
1225 | dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n"); | ||
1226 | return -EINVAL; | ||
1227 | |||
1228 | out_invalid_fh: | ||
1229 | dfprintk(MOUNT, "NFS: invalid root filehandle\n"); | ||
1230 | return -EINVAL; | ||
514 | } | 1231 | } |
515 | 1232 | ||
516 | /* | 1233 | /* |
@@ -600,13 +1317,51 @@ static int nfs_compare_super(struct super_block *sb, void *data) | |||
600 | { | 1317 | { |
601 | struct nfs_server *server = data, *old = NFS_SB(sb); | 1318 | struct nfs_server *server = data, *old = NFS_SB(sb); |
602 | 1319 | ||
603 | if (old->nfs_client != server->nfs_client) | 1320 | if (memcmp(&old->nfs_client->cl_addr, |
1321 | &server->nfs_client->cl_addr, | ||
1322 | sizeof(old->nfs_client->cl_addr)) != 0) | ||
1323 | return 0; | ||
1324 | /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ | ||
1325 | if (old->flags & NFS_MOUNT_UNSHARED) | ||
604 | return 0; | 1326 | return 0; |
605 | if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) | 1327 | if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) |
606 | return 0; | 1328 | return 0; |
607 | return 1; | 1329 | return 1; |
608 | } | 1330 | } |
609 | 1331 | ||
1332 | #define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) | ||
1333 | |||
1334 | static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) | ||
1335 | { | ||
1336 | const struct nfs_server *a = s->s_fs_info; | ||
1337 | const struct rpc_clnt *clnt_a = a->client; | ||
1338 | const struct rpc_clnt *clnt_b = b->client; | ||
1339 | |||
1340 | if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK)) | ||
1341 | goto Ebusy; | ||
1342 | if (a->nfs_client != b->nfs_client) | ||
1343 | goto Ebusy; | ||
1344 | if (a->flags != b->flags) | ||
1345 | goto Ebusy; | ||
1346 | if (a->wsize != b->wsize) | ||
1347 | goto Ebusy; | ||
1348 | if (a->rsize != b->rsize) | ||
1349 | goto Ebusy; | ||
1350 | if (a->acregmin != b->acregmin) | ||
1351 | goto Ebusy; | ||
1352 | if (a->acregmax != b->acregmax) | ||
1353 | goto Ebusy; | ||
1354 | if (a->acdirmin != b->acdirmin) | ||
1355 | goto Ebusy; | ||
1356 | if (a->acdirmax != b->acdirmax) | ||
1357 | goto Ebusy; | ||
1358 | if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) | ||
1359 | goto Ebusy; | ||
1360 | return 0; | ||
1361 | Ebusy: | ||
1362 | return -EBUSY; | ||
1363 | } | ||
1364 | |||
610 | static int nfs_get_sb(struct file_system_type *fs_type, | 1365 | static int nfs_get_sb(struct file_system_type *fs_type, |
611 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | 1366 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) |
612 | { | 1367 | { |
@@ -615,30 +1370,37 @@ static int nfs_get_sb(struct file_system_type *fs_type, | |||
615 | struct nfs_fh mntfh; | 1370 | struct nfs_fh mntfh; |
616 | struct nfs_mount_data *data = raw_data; | 1371 | struct nfs_mount_data *data = raw_data; |
617 | struct dentry *mntroot; | 1372 | struct dentry *mntroot; |
1373 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | ||
618 | int error; | 1374 | int error; |
619 | 1375 | ||
620 | /* Validate the mount data */ | 1376 | /* Validate the mount data */ |
621 | error = nfs_validate_mount_data(data, &mntfh); | 1377 | error = nfs_validate_mount_data(&data, &mntfh, dev_name); |
622 | if (error < 0) | 1378 | if (error < 0) |
623 | return error; | 1379 | goto out; |
624 | 1380 | ||
625 | /* Get a volume representation */ | 1381 | /* Get a volume representation */ |
626 | server = nfs_create_server(data, &mntfh); | 1382 | server = nfs_create_server(data, &mntfh); |
627 | if (IS_ERR(server)) { | 1383 | if (IS_ERR(server)) { |
628 | error = PTR_ERR(server); | 1384 | error = PTR_ERR(server); |
629 | goto out_err_noserver; | 1385 | goto out; |
630 | } | 1386 | } |
631 | 1387 | ||
1388 | if (server->flags & NFS_MOUNT_UNSHARED) | ||
1389 | compare_super = NULL; | ||
1390 | |||
632 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1391 | /* Get a superblock - note that we may end up sharing one that already exists */ |
633 | s = sget(fs_type, nfs_compare_super, nfs_set_super, server); | 1392 | s = sget(fs_type, compare_super, nfs_set_super, server); |
634 | if (IS_ERR(s)) { | 1393 | if (IS_ERR(s)) { |
635 | error = PTR_ERR(s); | 1394 | error = PTR_ERR(s); |
636 | goto out_err_nosb; | 1395 | goto out_err_nosb; |
637 | } | 1396 | } |
638 | 1397 | ||
639 | if (s->s_fs_info != server) { | 1398 | if (s->s_fs_info != server) { |
1399 | error = nfs_compare_mount_options(s, server, flags); | ||
640 | nfs_free_server(server); | 1400 | nfs_free_server(server); |
641 | server = NULL; | 1401 | server = NULL; |
1402 | if (error < 0) | ||
1403 | goto error_splat_super; | ||
642 | } | 1404 | } |
643 | 1405 | ||
644 | if (!s->s_root) { | 1406 | if (!s->s_root) { |
@@ -656,17 +1418,21 @@ static int nfs_get_sb(struct file_system_type *fs_type, | |||
656 | s->s_flags |= MS_ACTIVE; | 1418 | s->s_flags |= MS_ACTIVE; |
657 | mnt->mnt_sb = s; | 1419 | mnt->mnt_sb = s; |
658 | mnt->mnt_root = mntroot; | 1420 | mnt->mnt_root = mntroot; |
659 | return 0; | 1421 | error = 0; |
1422 | |||
1423 | out: | ||
1424 | if (data != raw_data) | ||
1425 | kfree(data); | ||
1426 | return error; | ||
660 | 1427 | ||
661 | out_err_nosb: | 1428 | out_err_nosb: |
662 | nfs_free_server(server); | 1429 | nfs_free_server(server); |
663 | out_err_noserver: | 1430 | goto out; |
664 | return error; | ||
665 | 1431 | ||
666 | error_splat_super: | 1432 | error_splat_super: |
667 | up_write(&s->s_umount); | 1433 | up_write(&s->s_umount); |
668 | deactivate_super(s); | 1434 | deactivate_super(s); |
669 | return error; | 1435 | goto out; |
670 | } | 1436 | } |
671 | 1437 | ||
672 | /* | 1438 | /* |
@@ -691,6 +1457,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
691 | struct super_block *s; | 1457 | struct super_block *s; |
692 | struct nfs_server *server; | 1458 | struct nfs_server *server; |
693 | struct dentry *mntroot; | 1459 | struct dentry *mntroot; |
1460 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | ||
694 | int error; | 1461 | int error; |
695 | 1462 | ||
696 | dprintk("--> nfs_xdev_get_sb()\n"); | 1463 | dprintk("--> nfs_xdev_get_sb()\n"); |
@@ -702,16 +1469,22 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
702 | goto out_err_noserver; | 1469 | goto out_err_noserver; |
703 | } | 1470 | } |
704 | 1471 | ||
1472 | if (server->flags & NFS_MOUNT_UNSHARED) | ||
1473 | compare_super = NULL; | ||
1474 | |||
705 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1475 | /* Get a superblock - note that we may end up sharing one that already exists */ |
706 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); | 1476 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); |
707 | if (IS_ERR(s)) { | 1477 | if (IS_ERR(s)) { |
708 | error = PTR_ERR(s); | 1478 | error = PTR_ERR(s); |
709 | goto out_err_nosb; | 1479 | goto out_err_nosb; |
710 | } | 1480 | } |
711 | 1481 | ||
712 | if (s->s_fs_info != server) { | 1482 | if (s->s_fs_info != server) { |
1483 | error = nfs_compare_mount_options(s, server, flags); | ||
713 | nfs_free_server(server); | 1484 | nfs_free_server(server); |
714 | server = NULL; | 1485 | server = NULL; |
1486 | if (error < 0) | ||
1487 | goto error_splat_super; | ||
715 | } | 1488 | } |
716 | 1489 | ||
717 | if (!s->s_root) { | 1490 | if (!s->s_root) { |
@@ -772,25 +1545,164 @@ static void nfs4_fill_super(struct super_block *sb) | |||
772 | nfs_initialise_sb(sb); | 1545 | nfs_initialise_sb(sb); |
773 | } | 1546 | } |
774 | 1547 | ||
775 | static void *nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) | 1548 | /* |
1549 | * Validate NFSv4 mount options | ||
1550 | */ | ||
1551 | static int nfs4_validate_mount_data(struct nfs4_mount_data **options, | ||
1552 | const char *dev_name, | ||
1553 | struct sockaddr_in *addr, | ||
1554 | rpc_authflavor_t *authflavour, | ||
1555 | char **hostname, | ||
1556 | char **mntpath, | ||
1557 | char **ip_addr) | ||
776 | { | 1558 | { |
777 | void *p = NULL; | 1559 | struct nfs4_mount_data *data = *options; |
778 | 1560 | char *c; | |
779 | if (!src->len) | 1561 | |
780 | return ERR_PTR(-EINVAL); | 1562 | if (data == NULL) |
781 | if (src->len < maxlen) | 1563 | goto out_no_data; |
782 | maxlen = src->len; | 1564 | |
783 | if (dst == NULL) { | 1565 | switch (data->version) { |
784 | p = dst = kmalloc(maxlen + 1, GFP_KERNEL); | 1566 | case 1: |
785 | if (p == NULL) | 1567 | if (data->host_addrlen != sizeof(*addr)) |
786 | return ERR_PTR(-ENOMEM); | 1568 | goto out_no_address; |
787 | } | 1569 | if (copy_from_user(addr, data->host_addr, sizeof(*addr))) |
788 | if (copy_from_user(dst, src->data, maxlen)) { | 1570 | return -EFAULT; |
789 | kfree(p); | 1571 | if (addr->sin_port == 0) |
790 | return ERR_PTR(-EFAULT); | 1572 | addr->sin_port = htons(NFS_PORT); |
1573 | if (!nfs_verify_server_address((struct sockaddr *) addr)) | ||
1574 | goto out_no_address; | ||
1575 | |||
1576 | switch (data->auth_flavourlen) { | ||
1577 | case 0: | ||
1578 | *authflavour = RPC_AUTH_UNIX; | ||
1579 | break; | ||
1580 | case 1: | ||
1581 | if (copy_from_user(authflavour, data->auth_flavours, | ||
1582 | sizeof(*authflavour))) | ||
1583 | return -EFAULT; | ||
1584 | break; | ||
1585 | default: | ||
1586 | goto out_inval_auth; | ||
1587 | } | ||
1588 | |||
1589 | c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN); | ||
1590 | if (IS_ERR(c)) | ||
1591 | return PTR_ERR(c); | ||
1592 | *hostname = c; | ||
1593 | |||
1594 | c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN); | ||
1595 | if (IS_ERR(c)) | ||
1596 | return PTR_ERR(c); | ||
1597 | *mntpath = c; | ||
1598 | dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *mntpath); | ||
1599 | |||
1600 | c = strndup_user(data->client_addr.data, 16); | ||
1601 | if (IS_ERR(c)) | ||
1602 | return PTR_ERR(c); | ||
1603 | *ip_addr = c; | ||
1604 | |||
1605 | break; | ||
1606 | default: { | ||
1607 | unsigned int len; | ||
1608 | struct nfs_parsed_mount_data args = { | ||
1609 | .rsize = NFS_MAX_FILE_IO_SIZE, | ||
1610 | .wsize = NFS_MAX_FILE_IO_SIZE, | ||
1611 | .timeo = 600, | ||
1612 | .retrans = 2, | ||
1613 | .acregmin = 3, | ||
1614 | .acregmax = 60, | ||
1615 | .acdirmin = 30, | ||
1616 | .acdirmax = 60, | ||
1617 | .nfs_server.protocol = IPPROTO_TCP, | ||
1618 | }; | ||
1619 | |||
1620 | if (nfs_parse_mount_options((char *) *options, &args) == 0) | ||
1621 | return -EINVAL; | ||
1622 | |||
1623 | if (!nfs_verify_server_address((struct sockaddr *) | ||
1624 | &args.nfs_server.address)) | ||
1625 | return -EINVAL; | ||
1626 | *addr = args.nfs_server.address; | ||
1627 | |||
1628 | switch (args.auth_flavor_len) { | ||
1629 | case 0: | ||
1630 | *authflavour = RPC_AUTH_UNIX; | ||
1631 | break; | ||
1632 | case 1: | ||
1633 | *authflavour = (rpc_authflavor_t) args.auth_flavors[0]; | ||
1634 | break; | ||
1635 | default: | ||
1636 | goto out_inval_auth; | ||
1637 | } | ||
1638 | |||
1639 | /* | ||
1640 | * Translate to nfs4_mount_data, which nfs4_fill_super | ||
1641 | * can deal with. | ||
1642 | */ | ||
1643 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
1644 | if (data == NULL) | ||
1645 | return -ENOMEM; | ||
1646 | *options = data; | ||
1647 | |||
1648 | data->version = 1; | ||
1649 | data->flags = args.flags & NFS4_MOUNT_FLAGMASK; | ||
1650 | data->rsize = args.rsize; | ||
1651 | data->wsize = args.wsize; | ||
1652 | data->timeo = args.timeo; | ||
1653 | data->retrans = args.retrans; | ||
1654 | data->acregmin = args.acregmin; | ||
1655 | data->acregmax = args.acregmax; | ||
1656 | data->acdirmin = args.acdirmin; | ||
1657 | data->acdirmax = args.acdirmax; | ||
1658 | data->proto = args.nfs_server.protocol; | ||
1659 | |||
1660 | /* | ||
1661 | * Split "dev_name" into "hostname:mntpath". | ||
1662 | */ | ||
1663 | c = strchr(dev_name, ':'); | ||
1664 | if (c == NULL) | ||
1665 | return -EINVAL; | ||
1666 | /* while calculating len, pretend ':' is '\0' */ | ||
1667 | len = c - dev_name; | ||
1668 | if (len > NFS4_MAXNAMLEN) | ||
1669 | return -EINVAL; | ||
1670 | *hostname = kzalloc(len, GFP_KERNEL); | ||
1671 | if (*hostname == NULL) | ||
1672 | return -ENOMEM; | ||
1673 | strncpy(*hostname, dev_name, len - 1); | ||
1674 | |||
1675 | c++; /* step over the ':' */ | ||
1676 | len = strlen(c); | ||
1677 | if (len > NFS4_MAXPATHLEN) | ||
1678 | return -EINVAL; | ||
1679 | *mntpath = kzalloc(len + 1, GFP_KERNEL); | ||
1680 | if (*mntpath == NULL) | ||
1681 | return -ENOMEM; | ||
1682 | strncpy(*mntpath, c, len); | ||
1683 | |||
1684 | dprintk("MNTPATH: %s\n", *mntpath); | ||
1685 | |||
1686 | *ip_addr = args.client_address; | ||
1687 | |||
1688 | break; | ||
1689 | } | ||
791 | } | 1690 | } |
792 | dst[maxlen] = '\0'; | 1691 | |
793 | return dst; | 1692 | return 0; |
1693 | |||
1694 | out_no_data: | ||
1695 | dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n"); | ||
1696 | return -EINVAL; | ||
1697 | |||
1698 | out_inval_auth: | ||
1699 | dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n", | ||
1700 | data->auth_flavourlen); | ||
1701 | return -EINVAL; | ||
1702 | |||
1703 | out_no_address: | ||
1704 | dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); | ||
1705 | return -EINVAL; | ||
794 | } | 1706 | } |
795 | 1707 | ||
796 | /* | 1708 | /* |
@@ -806,81 +1718,29 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
806 | rpc_authflavor_t authflavour; | 1718 | rpc_authflavor_t authflavour; |
807 | struct nfs_fh mntfh; | 1719 | struct nfs_fh mntfh; |
808 | struct dentry *mntroot; | 1720 | struct dentry *mntroot; |
809 | char *mntpath = NULL, *hostname = NULL, ip_addr[16]; | 1721 | char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL; |
810 | void *p; | 1722 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
811 | int error; | 1723 | int error; |
812 | 1724 | ||
813 | if (data == NULL) { | 1725 | /* Validate the mount data */ |
814 | dprintk("%s: missing data argument\n", __FUNCTION__); | 1726 | error = nfs4_validate_mount_data(&data, dev_name, &addr, &authflavour, |
815 | return -EINVAL; | 1727 | &hostname, &mntpath, &ip_addr); |
816 | } | 1728 | if (error < 0) |
817 | if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) { | 1729 | goto out; |
818 | dprintk("%s: bad mount version\n", __FUNCTION__); | ||
819 | return -EINVAL; | ||
820 | } | ||
821 | |||
822 | /* We now require that the mount process passes the remote address */ | ||
823 | if (data->host_addrlen != sizeof(addr)) | ||
824 | return -EINVAL; | ||
825 | |||
826 | if (copy_from_user(&addr, data->host_addr, sizeof(addr))) | ||
827 | return -EFAULT; | ||
828 | |||
829 | if (addr.sin_family != AF_INET || | ||
830 | addr.sin_addr.s_addr == INADDR_ANY | ||
831 | ) { | ||
832 | dprintk("%s: mount program didn't pass remote IP address!\n", | ||
833 | __FUNCTION__); | ||
834 | return -EINVAL; | ||
835 | } | ||
836 | /* RFC3530: The default port for NFS is 2049 */ | ||
837 | if (addr.sin_port == 0) | ||
838 | addr.sin_port = htons(NFS_PORT); | ||
839 | |||
840 | /* Grab the authentication type */ | ||
841 | authflavour = RPC_AUTH_UNIX; | ||
842 | if (data->auth_flavourlen != 0) { | ||
843 | if (data->auth_flavourlen != 1) { | ||
844 | dprintk("%s: Invalid number of RPC auth flavours %d.\n", | ||
845 | __FUNCTION__, data->auth_flavourlen); | ||
846 | error = -EINVAL; | ||
847 | goto out_err_noserver; | ||
848 | } | ||
849 | |||
850 | if (copy_from_user(&authflavour, data->auth_flavours, | ||
851 | sizeof(authflavour))) { | ||
852 | error = -EFAULT; | ||
853 | goto out_err_noserver; | ||
854 | } | ||
855 | } | ||
856 | |||
857 | p = nfs_copy_user_string(NULL, &data->hostname, 256); | ||
858 | if (IS_ERR(p)) | ||
859 | goto out_err; | ||
860 | hostname = p; | ||
861 | |||
862 | p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); | ||
863 | if (IS_ERR(p)) | ||
864 | goto out_err; | ||
865 | mntpath = p; | ||
866 | |||
867 | dprintk("MNTPATH: %s\n", mntpath); | ||
868 | |||
869 | p = nfs_copy_user_string(ip_addr, &data->client_addr, | ||
870 | sizeof(ip_addr) - 1); | ||
871 | if (IS_ERR(p)) | ||
872 | goto out_err; | ||
873 | 1730 | ||
874 | /* Get a volume representation */ | 1731 | /* Get a volume representation */ |
875 | server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr, | 1732 | server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr, |
876 | authflavour, &mntfh); | 1733 | authflavour, &mntfh); |
877 | if (IS_ERR(server)) { | 1734 | if (IS_ERR(server)) { |
878 | error = PTR_ERR(server); | 1735 | error = PTR_ERR(server); |
879 | goto out_err_noserver; | 1736 | goto out; |
880 | } | 1737 | } |
881 | 1738 | ||
1739 | if (server->flags & NFS4_MOUNT_UNSHARED) | ||
1740 | compare_super = NULL; | ||
1741 | |||
882 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1742 | /* Get a superblock - note that we may end up sharing one that already exists */ |
883 | s = sget(fs_type, nfs_compare_super, nfs_set_super, server); | 1743 | s = sget(fs_type, compare_super, nfs_set_super, server); |
884 | if (IS_ERR(s)) { | 1744 | if (IS_ERR(s)) { |
885 | error = PTR_ERR(s); | 1745 | error = PTR_ERR(s); |
886 | goto out_free; | 1746 | goto out_free; |
@@ -906,25 +1766,22 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
906 | s->s_flags |= MS_ACTIVE; | 1766 | s->s_flags |= MS_ACTIVE; |
907 | mnt->mnt_sb = s; | 1767 | mnt->mnt_sb = s; |
908 | mnt->mnt_root = mntroot; | 1768 | mnt->mnt_root = mntroot; |
1769 | error = 0; | ||
1770 | |||
1771 | out: | ||
1772 | kfree(ip_addr); | ||
909 | kfree(mntpath); | 1773 | kfree(mntpath); |
910 | kfree(hostname); | 1774 | kfree(hostname); |
911 | return 0; | 1775 | return error; |
912 | |||
913 | out_err: | ||
914 | error = PTR_ERR(p); | ||
915 | goto out_err_noserver; | ||
916 | 1776 | ||
917 | out_free: | 1777 | out_free: |
918 | nfs_free_server(server); | 1778 | nfs_free_server(server); |
919 | out_err_noserver: | 1779 | goto out; |
920 | kfree(mntpath); | ||
921 | kfree(hostname); | ||
922 | return error; | ||
923 | 1780 | ||
924 | error_splat_super: | 1781 | error_splat_super: |
925 | up_write(&s->s_umount); | 1782 | up_write(&s->s_umount); |
926 | deactivate_super(s); | 1783 | deactivate_super(s); |
927 | goto out_err_noserver; | 1784 | goto out; |
928 | } | 1785 | } |
929 | 1786 | ||
930 | static void nfs4_kill_super(struct super_block *sb) | 1787 | static void nfs4_kill_super(struct super_block *sb) |
@@ -949,6 +1806,7 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
949 | struct super_block *s; | 1806 | struct super_block *s; |
950 | struct nfs_server *server; | 1807 | struct nfs_server *server; |
951 | struct dentry *mntroot; | 1808 | struct dentry *mntroot; |
1809 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | ||
952 | int error; | 1810 | int error; |
953 | 1811 | ||
954 | dprintk("--> nfs4_xdev_get_sb()\n"); | 1812 | dprintk("--> nfs4_xdev_get_sb()\n"); |
@@ -960,8 +1818,11 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
960 | goto out_err_noserver; | 1818 | goto out_err_noserver; |
961 | } | 1819 | } |
962 | 1820 | ||
1821 | if (server->flags & NFS4_MOUNT_UNSHARED) | ||
1822 | compare_super = NULL; | ||
1823 | |||
963 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1824 | /* Get a superblock - note that we may end up sharing one that already exists */ |
964 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); | 1825 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); |
965 | if (IS_ERR(s)) { | 1826 | if (IS_ERR(s)) { |
966 | error = PTR_ERR(s); | 1827 | error = PTR_ERR(s); |
967 | goto out_err_nosb; | 1828 | goto out_err_nosb; |
@@ -1016,6 +1877,7 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |||
1016 | struct nfs_server *server; | 1877 | struct nfs_server *server; |
1017 | struct dentry *mntroot; | 1878 | struct dentry *mntroot; |
1018 | struct nfs_fh mntfh; | 1879 | struct nfs_fh mntfh; |
1880 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | ||
1019 | int error; | 1881 | int error; |
1020 | 1882 | ||
1021 | dprintk("--> nfs4_referral_get_sb()\n"); | 1883 | dprintk("--> nfs4_referral_get_sb()\n"); |
@@ -1027,8 +1889,11 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |||
1027 | goto out_err_noserver; | 1889 | goto out_err_noserver; |
1028 | } | 1890 | } |
1029 | 1891 | ||
1892 | if (server->flags & NFS4_MOUNT_UNSHARED) | ||
1893 | compare_super = NULL; | ||
1894 | |||
1030 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1895 | /* Get a superblock - note that we may end up sharing one that already exists */ |
1031 | s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); | 1896 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); |
1032 | if (IS_ERR(s)) { | 1897 | if (IS_ERR(s)) { |
1033 | error = PTR_ERR(s); | 1898 | error = PTR_ERR(s); |
1034 | goto out_err_nosb; | 1899 | goto out_err_nosb; |