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; |
