diff options
Diffstat (limited to 'fs/nfsd/nfssvc.c')
| -rw-r--r-- | fs/nfsd/nfssvc.c | 214 |
1 files changed, 168 insertions, 46 deletions
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 378edcfe9701..520757774614 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
| @@ -38,12 +38,18 @@ static int nfsd_acl_rpcbind_set(struct net *, | |||
| 38 | u32, int, | 38 | u32, int, |
| 39 | unsigned short, | 39 | unsigned short, |
| 40 | unsigned short); | 40 | unsigned short); |
| 41 | static __be32 nfsd_acl_init_request(struct svc_rqst *, | ||
| 42 | const struct svc_program *, | ||
| 43 | struct svc_process_info *); | ||
| 41 | #endif | 44 | #endif |
| 42 | static int nfsd_rpcbind_set(struct net *, | 45 | static int nfsd_rpcbind_set(struct net *, |
| 43 | const struct svc_program *, | 46 | const struct svc_program *, |
| 44 | u32, int, | 47 | u32, int, |
| 45 | unsigned short, | 48 | unsigned short, |
| 46 | unsigned short); | 49 | unsigned short); |
| 50 | static __be32 nfsd_init_request(struct svc_rqst *, | ||
| 51 | const struct svc_program *, | ||
| 52 | struct svc_process_info *); | ||
| 47 | 53 | ||
| 48 | /* | 54 | /* |
| 49 | * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members | 55 | * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members |
| @@ -98,7 +104,7 @@ static struct svc_program nfsd_acl_program = { | |||
| 98 | .pg_class = "nfsd", | 104 | .pg_class = "nfsd", |
| 99 | .pg_stats = &nfsd_acl_svcstats, | 105 | .pg_stats = &nfsd_acl_svcstats, |
| 100 | .pg_authenticate = &svc_set_client, | 106 | .pg_authenticate = &svc_set_client, |
| 101 | .pg_init_request = svc_generic_init_request, | 107 | .pg_init_request = nfsd_acl_init_request, |
| 102 | .pg_rpcbind_set = nfsd_acl_rpcbind_set, | 108 | .pg_rpcbind_set = nfsd_acl_rpcbind_set, |
| 103 | }; | 109 | }; |
| 104 | 110 | ||
| @@ -119,7 +125,6 @@ static const struct svc_version *nfsd_version[] = { | |||
| 119 | 125 | ||
| 120 | #define NFSD_MINVERS 2 | 126 | #define NFSD_MINVERS 2 |
| 121 | #define NFSD_NRVERS ARRAY_SIZE(nfsd_version) | 127 | #define NFSD_NRVERS ARRAY_SIZE(nfsd_version) |
| 122 | static const struct svc_version *nfsd_versions[NFSD_NRVERS]; | ||
| 123 | 128 | ||
| 124 | struct svc_program nfsd_program = { | 129 | struct svc_program nfsd_program = { |
| 125 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 130 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
| @@ -127,78 +132,136 @@ struct svc_program nfsd_program = { | |||
| 127 | #endif | 132 | #endif |
| 128 | .pg_prog = NFS_PROGRAM, /* program number */ | 133 | .pg_prog = NFS_PROGRAM, /* program number */ |
| 129 | .pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */ | 134 | .pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */ |
| 130 | .pg_vers = nfsd_versions, /* version table */ | 135 | .pg_vers = nfsd_version, /* version table */ |
| 131 | .pg_name = "nfsd", /* program name */ | 136 | .pg_name = "nfsd", /* program name */ |
| 132 | .pg_class = "nfsd", /* authentication class */ | 137 | .pg_class = "nfsd", /* authentication class */ |
| 133 | .pg_stats = &nfsd_svcstats, /* version table */ | 138 | .pg_stats = &nfsd_svcstats, /* version table */ |
| 134 | .pg_authenticate = &svc_set_client, /* export authentication */ | 139 | .pg_authenticate = &svc_set_client, /* export authentication */ |
| 135 | .pg_init_request = svc_generic_init_request, | 140 | .pg_init_request = nfsd_init_request, |
| 136 | .pg_rpcbind_set = nfsd_rpcbind_set, | 141 | .pg_rpcbind_set = nfsd_rpcbind_set, |
| 137 | }; | 142 | }; |
| 138 | 143 | ||
| 139 | static bool nfsd_supported_minorversions[NFSD_SUPPORTED_MINOR_VERSION + 1] = { | 144 | static bool |
| 140 | [0] = 1, | 145 | nfsd_support_version(int vers) |
| 141 | [1] = 1, | 146 | { |
| 142 | [2] = 1, | 147 | if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS) |
| 143 | }; | 148 | return nfsd_version[vers] != NULL; |
| 149 | return false; | ||
| 150 | } | ||
| 151 | |||
| 152 | static bool * | ||
| 153 | nfsd_alloc_versions(void) | ||
| 154 | { | ||
| 155 | bool *vers = kmalloc_array(NFSD_NRVERS, sizeof(bool), GFP_KERNEL); | ||
| 156 | unsigned i; | ||
| 157 | |||
| 158 | if (vers) { | ||
| 159 | /* All compiled versions are enabled by default */ | ||
| 160 | for (i = 0; i < NFSD_NRVERS; i++) | ||
| 161 | vers[i] = nfsd_support_version(i); | ||
| 162 | } | ||
| 163 | return vers; | ||
| 164 | } | ||
| 165 | |||
| 166 | static bool * | ||
| 167 | nfsd_alloc_minorversions(void) | ||
| 168 | { | ||
| 169 | bool *vers = kmalloc_array(NFSD_SUPPORTED_MINOR_VERSION + 1, | ||
| 170 | sizeof(bool), GFP_KERNEL); | ||
| 171 | unsigned i; | ||
| 144 | 172 | ||
| 145 | int nfsd_vers(int vers, enum vers_op change) | 173 | if (vers) { |
| 174 | /* All minor versions are enabled by default */ | ||
| 175 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) | ||
| 176 | vers[i] = nfsd_support_version(4); | ||
| 177 | } | ||
| 178 | return vers; | ||
| 179 | } | ||
| 180 | |||
| 181 | void | ||
| 182 | nfsd_netns_free_versions(struct nfsd_net *nn) | ||
| 183 | { | ||
| 184 | kfree(nn->nfsd_versions); | ||
| 185 | kfree(nn->nfsd4_minorversions); | ||
| 186 | nn->nfsd_versions = NULL; | ||
| 187 | nn->nfsd4_minorversions = NULL; | ||
| 188 | } | ||
| 189 | |||
| 190 | static void | ||
| 191 | nfsd_netns_init_versions(struct nfsd_net *nn) | ||
| 192 | { | ||
| 193 | if (!nn->nfsd_versions) { | ||
| 194 | nn->nfsd_versions = nfsd_alloc_versions(); | ||
| 195 | nn->nfsd4_minorversions = nfsd_alloc_minorversions(); | ||
| 196 | if (!nn->nfsd_versions || !nn->nfsd4_minorversions) | ||
| 197 | nfsd_netns_free_versions(nn); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change) | ||
| 146 | { | 202 | { |
| 147 | if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) | 203 | if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) |
| 148 | return 0; | 204 | return 0; |
| 149 | switch(change) { | 205 | switch(change) { |
| 150 | case NFSD_SET: | 206 | case NFSD_SET: |
| 151 | nfsd_versions[vers] = nfsd_version[vers]; | 207 | if (nn->nfsd_versions) |
| 152 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 208 | nn->nfsd_versions[vers] = nfsd_support_version(vers); |
| 153 | if (vers < NFSD_ACL_NRVERS) | ||
| 154 | nfsd_acl_versions[vers] = nfsd_acl_version[vers]; | ||
| 155 | #endif | ||
| 156 | break; | 209 | break; |
| 157 | case NFSD_CLEAR: | 210 | case NFSD_CLEAR: |
| 158 | nfsd_versions[vers] = NULL; | 211 | nfsd_netns_init_versions(nn); |
| 159 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 212 | if (nn->nfsd_versions) |
| 160 | if (vers < NFSD_ACL_NRVERS) | 213 | nn->nfsd_versions[vers] = false; |
| 161 | nfsd_acl_versions[vers] = NULL; | ||
| 162 | #endif | ||
| 163 | break; | 214 | break; |
| 164 | case NFSD_TEST: | 215 | case NFSD_TEST: |
| 165 | return nfsd_versions[vers] != NULL; | 216 | if (nn->nfsd_versions) |
| 217 | return nn->nfsd_versions[vers]; | ||
| 218 | /* Fallthrough */ | ||
| 166 | case NFSD_AVAIL: | 219 | case NFSD_AVAIL: |
| 167 | return nfsd_version[vers] != NULL; | 220 | return nfsd_support_version(vers); |
| 168 | } | 221 | } |
| 169 | return 0; | 222 | return 0; |
| 170 | } | 223 | } |
| 171 | 224 | ||
| 172 | static void | 225 | static void |
| 173 | nfsd_adjust_nfsd_versions4(void) | 226 | nfsd_adjust_nfsd_versions4(struct nfsd_net *nn) |
| 174 | { | 227 | { |
| 175 | unsigned i; | 228 | unsigned i; |
| 176 | 229 | ||
| 177 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) { | 230 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) { |
| 178 | if (nfsd_supported_minorversions[i]) | 231 | if (nn->nfsd4_minorversions[i]) |
| 179 | return; | 232 | return; |
| 180 | } | 233 | } |
| 181 | nfsd_vers(4, NFSD_CLEAR); | 234 | nfsd_vers(nn, 4, NFSD_CLEAR); |
| 182 | } | 235 | } |
| 183 | 236 | ||
| 184 | int nfsd_minorversion(u32 minorversion, enum vers_op change) | 237 | int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change) |
| 185 | { | 238 | { |
| 186 | if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && | 239 | if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && |
| 187 | change != NFSD_AVAIL) | 240 | change != NFSD_AVAIL) |
| 188 | return -1; | 241 | return -1; |
| 242 | |||
| 189 | switch(change) { | 243 | switch(change) { |
| 190 | case NFSD_SET: | 244 | case NFSD_SET: |
| 191 | nfsd_supported_minorversions[minorversion] = true; | 245 | if (nn->nfsd4_minorversions) { |
| 192 | nfsd_vers(4, NFSD_SET); | 246 | nfsd_vers(nn, 4, NFSD_SET); |
| 247 | nn->nfsd4_minorversions[minorversion] = | ||
| 248 | nfsd_vers(nn, 4, NFSD_TEST); | ||
| 249 | } | ||
| 193 | break; | 250 | break; |
| 194 | case NFSD_CLEAR: | 251 | case NFSD_CLEAR: |
| 195 | nfsd_supported_minorversions[minorversion] = false; | 252 | nfsd_netns_init_versions(nn); |
| 196 | nfsd_adjust_nfsd_versions4(); | 253 | if (nn->nfsd4_minorversions) { |
| 254 | nn->nfsd4_minorversions[minorversion] = false; | ||
| 255 | nfsd_adjust_nfsd_versions4(nn); | ||
| 256 | } | ||
| 197 | break; | 257 | break; |
| 198 | case NFSD_TEST: | 258 | case NFSD_TEST: |
| 199 | return nfsd_supported_minorversions[minorversion]; | 259 | if (nn->nfsd4_minorversions) |
| 260 | return nn->nfsd4_minorversions[minorversion]; | ||
| 261 | return nfsd_vers(nn, 4, NFSD_TEST); | ||
| 200 | case NFSD_AVAIL: | 262 | case NFSD_AVAIL: |
| 201 | return minorversion <= NFSD_SUPPORTED_MINOR_VERSION; | 263 | return minorversion <= NFSD_SUPPORTED_MINOR_VERSION && |
| 264 | nfsd_vers(nn, 4, NFSD_AVAIL); | ||
| 202 | } | 265 | } |
| 203 | return 0; | 266 | return 0; |
| 204 | } | 267 | } |
| @@ -280,13 +343,9 @@ static void nfsd_shutdown_generic(void) | |||
| 280 | nfsd_racache_shutdown(); | 343 | nfsd_racache_shutdown(); |
| 281 | } | 344 | } |
| 282 | 345 | ||
| 283 | static bool nfsd_needs_lockd(void) | 346 | static bool nfsd_needs_lockd(struct nfsd_net *nn) |
| 284 | { | 347 | { |
| 285 | #if defined(CONFIG_NFSD_V3) | 348 | return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST); |
| 286 | return (nfsd_versions[2] != NULL) || (nfsd_versions[3] != NULL); | ||
| 287 | #else | ||
| 288 | return (nfsd_versions[2] != NULL); | ||
| 289 | #endif | ||
| 290 | } | 349 | } |
| 291 | 350 | ||
| 292 | static int nfsd_startup_net(int nrservs, struct net *net) | 351 | static int nfsd_startup_net(int nrservs, struct net *net) |
| @@ -304,7 +363,7 @@ static int nfsd_startup_net(int nrservs, struct net *net) | |||
| 304 | if (ret) | 363 | if (ret) |
| 305 | goto out_socks; | 364 | goto out_socks; |
| 306 | 365 | ||
| 307 | if (nfsd_needs_lockd() && !nn->lockd_up) { | 366 | if (nfsd_needs_lockd(nn) && !nn->lockd_up) { |
| 308 | ret = lockd_up(net); | 367 | ret = lockd_up(net); |
| 309 | if (ret) | 368 | if (ret) |
| 310 | goto out_socks; | 369 | goto out_socks; |
| @@ -437,20 +496,20 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) | |||
| 437 | nfsd_export_flush(net); | 496 | nfsd_export_flush(net); |
| 438 | } | 497 | } |
| 439 | 498 | ||
| 440 | void nfsd_reset_versions(void) | 499 | void nfsd_reset_versions(struct nfsd_net *nn) |
| 441 | { | 500 | { |
| 442 | int i; | 501 | int i; |
| 443 | 502 | ||
| 444 | for (i = 0; i < NFSD_NRVERS; i++) | 503 | for (i = 0; i < NFSD_NRVERS; i++) |
| 445 | if (nfsd_vers(i, NFSD_TEST)) | 504 | if (nfsd_vers(nn, i, NFSD_TEST)) |
| 446 | return; | 505 | return; |
| 447 | 506 | ||
| 448 | for (i = 0; i < NFSD_NRVERS; i++) | 507 | for (i = 0; i < NFSD_NRVERS; i++) |
| 449 | if (i != 4) | 508 | if (i != 4) |
| 450 | nfsd_vers(i, NFSD_SET); | 509 | nfsd_vers(nn, i, NFSD_SET); |
| 451 | else { | 510 | else { |
| 452 | int minor = 0; | 511 | int minor = 0; |
| 453 | while (nfsd_minorversion(minor, NFSD_SET) >= 0) | 512 | while (nfsd_minorversion(nn, minor, NFSD_SET) >= 0) |
| 454 | minor++; | 513 | minor++; |
| 455 | } | 514 | } |
| 456 | } | 515 | } |
| @@ -518,7 +577,7 @@ int nfsd_create_serv(struct net *net) | |||
| 518 | } | 577 | } |
| 519 | if (nfsd_max_blksize == 0) | 578 | if (nfsd_max_blksize == 0) |
| 520 | nfsd_max_blksize = nfsd_get_default_max_blksize(); | 579 | nfsd_max_blksize = nfsd_get_default_max_blksize(); |
| 521 | nfsd_reset_versions(); | 580 | nfsd_reset_versions(nn); |
| 522 | nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, | 581 | nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, |
| 523 | &nfsd_thread_sv_ops); | 582 | &nfsd_thread_sv_ops); |
| 524 | if (nn->nfsd_serv == NULL) | 583 | if (nn->nfsd_serv == NULL) |
| @@ -697,11 +756,44 @@ nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp, | |||
| 697 | unsigned short port) | 756 | unsigned short port) |
| 698 | { | 757 | { |
| 699 | if (!nfsd_support_acl_version(version) || | 758 | if (!nfsd_support_acl_version(version) || |
| 700 | !nfsd_vers(version, NFSD_TEST)) | 759 | !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) |
| 701 | return 0; | 760 | return 0; |
| 702 | return svc_generic_rpcbind_set(net, progp, version, family, | 761 | return svc_generic_rpcbind_set(net, progp, version, family, |
| 703 | proto, port); | 762 | proto, port); |
| 704 | } | 763 | } |
| 764 | |||
| 765 | static __be32 | ||
| 766 | nfsd_acl_init_request(struct svc_rqst *rqstp, | ||
| 767 | const struct svc_program *progp, | ||
| 768 | struct svc_process_info *ret) | ||
| 769 | { | ||
| 770 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
| 771 | int i; | ||
| 772 | |||
| 773 | if (likely(nfsd_support_acl_version(rqstp->rq_vers) && | ||
| 774 | nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) | ||
| 775 | return svc_generic_init_request(rqstp, progp, ret); | ||
| 776 | |||
| 777 | ret->mismatch.lovers = NFSD_ACL_NRVERS; | ||
| 778 | for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) { | ||
| 779 | if (nfsd_support_acl_version(rqstp->rq_vers) && | ||
| 780 | nfsd_vers(nn, i, NFSD_TEST)) { | ||
| 781 | ret->mismatch.lovers = i; | ||
| 782 | break; | ||
| 783 | } | ||
| 784 | } | ||
| 785 | if (ret->mismatch.lovers == NFSD_ACL_NRVERS) | ||
| 786 | return rpc_prog_unavail; | ||
| 787 | ret->mismatch.hivers = NFSD_ACL_MINVERS; | ||
| 788 | for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) { | ||
| 789 | if (nfsd_support_acl_version(rqstp->rq_vers) && | ||
| 790 | nfsd_vers(nn, i, NFSD_TEST)) { | ||
| 791 | ret->mismatch.hivers = i; | ||
| 792 | break; | ||
| 793 | } | ||
| 794 | } | ||
| 795 | return rpc_prog_mismatch; | ||
| 796 | } | ||
| 705 | #endif | 797 | #endif |
| 706 | 798 | ||
| 707 | static int | 799 | static int |
| @@ -709,12 +801,42 @@ nfsd_rpcbind_set(struct net *net, const struct svc_program *progp, | |||
| 709 | u32 version, int family, unsigned short proto, | 801 | u32 version, int family, unsigned short proto, |
| 710 | unsigned short port) | 802 | unsigned short port) |
| 711 | { | 803 | { |
| 712 | if (!nfsd_vers(version, NFSD_TEST)) | 804 | if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) |
| 713 | return 0; | 805 | return 0; |
| 714 | return svc_generic_rpcbind_set(net, progp, version, family, | 806 | return svc_generic_rpcbind_set(net, progp, version, family, |
| 715 | proto, port); | 807 | proto, port); |
| 716 | } | 808 | } |
| 717 | 809 | ||
| 810 | static __be32 | ||
| 811 | nfsd_init_request(struct svc_rqst *rqstp, | ||
| 812 | const struct svc_program *progp, | ||
| 813 | struct svc_process_info *ret) | ||
| 814 | { | ||
| 815 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
| 816 | int i; | ||
| 817 | |||
| 818 | if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) | ||
| 819 | return svc_generic_init_request(rqstp, progp, ret); | ||
| 820 | |||
| 821 | ret->mismatch.lovers = NFSD_NRVERS; | ||
| 822 | for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) { | ||
| 823 | if (nfsd_vers(nn, i, NFSD_TEST)) { | ||
| 824 | ret->mismatch.lovers = i; | ||
| 825 | break; | ||
| 826 | } | ||
| 827 | } | ||
| 828 | if (ret->mismatch.lovers == NFSD_NRVERS) | ||
| 829 | return rpc_prog_unavail; | ||
| 830 | ret->mismatch.hivers = NFSD_MINVERS; | ||
| 831 | for (i = NFSD_NRVERS - 1; i >= NFSD_MINVERS; i--) { | ||
| 832 | if (nfsd_vers(nn, i, NFSD_TEST)) { | ||
| 833 | ret->mismatch.hivers = i; | ||
| 834 | break; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | return rpc_prog_mismatch; | ||
| 838 | } | ||
| 839 | |||
| 718 | /* | 840 | /* |
| 719 | * This is the NFS server kernel thread | 841 | * This is the NFS server kernel thread |
| 720 | */ | 842 | */ |
