diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-09-14 20:04:48 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-09-14 20:04:48 -0400 |
commit | de8d4f5d758786a2cbcfa54a6a85ce747e5637e3 (patch) | |
tree | a09c0a4f8fe995bea2dde0ea4fa5dbdd32cf3fd4 /net | |
parent | 75e1c70fc31490ef8a373ea2a4bea2524099b478 (diff) | |
parent | 827e3457022d0bb0b1bb8a0eb88501876fe7dcf0 (diff) |
Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6:
SUNRPC: Fix the NFSv4 and RPCSEC_GSS Kconfig dependencies
statfs() gives ESTALE error
NFS: Fix a typo in nfs_sockaddr_match_ipaddr6
sunrpc: increase MAX_HASHTABLE_BITS to 14
gss:spkm3 miss returning error to caller when import security context
gss:krb5 miss returning error to caller when import security context
Remove incorrect do_vfs_lock message
SUNRPC: cleanup state-machine ordering
SUNRPC: Fix a race in rpc_info_open
SUNRPC: Fix race corrupting rpc upcall
Fix null dereference in call_allocate
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/auth.c | 2 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 9 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_mech.c | 10 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_spkm3_mech.c | 5 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 116 | ||||
-rw-r--r-- | net/sunrpc/rpc_pipe.c | 20 |
6 files changed, 86 insertions, 76 deletions
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 36cb66022a27..e9eaaf7d43c1 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c | |||
@@ -38,7 +38,7 @@ static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { | |||
38 | static LIST_HEAD(cred_unused); | 38 | static LIST_HEAD(cred_unused); |
39 | static unsigned long number_cred_unused; | 39 | static unsigned long number_cred_unused; |
40 | 40 | ||
41 | #define MAX_HASHTABLE_BITS (10) | 41 | #define MAX_HASHTABLE_BITS (14) |
42 | static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) | 42 | static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) |
43 | { | 43 | { |
44 | unsigned long num; | 44 | unsigned long num; |
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index dcfc66bab2bb..12c485982814 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -745,17 +745,18 @@ gss_pipe_release(struct inode *inode) | |||
745 | struct rpc_inode *rpci = RPC_I(inode); | 745 | struct rpc_inode *rpci = RPC_I(inode); |
746 | struct gss_upcall_msg *gss_msg; | 746 | struct gss_upcall_msg *gss_msg; |
747 | 747 | ||
748 | restart: | ||
748 | spin_lock(&inode->i_lock); | 749 | spin_lock(&inode->i_lock); |
749 | while (!list_empty(&rpci->in_downcall)) { | 750 | list_for_each_entry(gss_msg, &rpci->in_downcall, list) { |
750 | 751 | ||
751 | gss_msg = list_entry(rpci->in_downcall.next, | 752 | if (!list_empty(&gss_msg->msg.list)) |
752 | struct gss_upcall_msg, list); | 753 | continue; |
753 | gss_msg->msg.errno = -EPIPE; | 754 | gss_msg->msg.errno = -EPIPE; |
754 | atomic_inc(&gss_msg->count); | 755 | atomic_inc(&gss_msg->count); |
755 | __gss_unhash_msg(gss_msg); | 756 | __gss_unhash_msg(gss_msg); |
756 | spin_unlock(&inode->i_lock); | 757 | spin_unlock(&inode->i_lock); |
757 | gss_release_msg(gss_msg); | 758 | gss_release_msg(gss_msg); |
758 | spin_lock(&inode->i_lock); | 759 | goto restart; |
759 | } | 760 | } |
760 | spin_unlock(&inode->i_lock); | 761 | spin_unlock(&inode->i_lock); |
761 | 762 | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 032644610524..778e5dfc5144 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c | |||
@@ -237,6 +237,7 @@ get_key(const void *p, const void *end, | |||
237 | if (!supported_gss_krb5_enctype(alg)) { | 237 | if (!supported_gss_krb5_enctype(alg)) { |
238 | printk(KERN_WARNING "gss_kerberos_mech: unsupported " | 238 | printk(KERN_WARNING "gss_kerberos_mech: unsupported " |
239 | "encryption key algorithm %d\n", alg); | 239 | "encryption key algorithm %d\n", alg); |
240 | p = ERR_PTR(-EINVAL); | ||
240 | goto out_err; | 241 | goto out_err; |
241 | } | 242 | } |
242 | p = simple_get_netobj(p, end, &key); | 243 | p = simple_get_netobj(p, end, &key); |
@@ -282,15 +283,19 @@ gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx) | |||
282 | ctx->enctype = ENCTYPE_DES_CBC_RAW; | 283 | ctx->enctype = ENCTYPE_DES_CBC_RAW; |
283 | 284 | ||
284 | ctx->gk5e = get_gss_krb5_enctype(ctx->enctype); | 285 | ctx->gk5e = get_gss_krb5_enctype(ctx->enctype); |
285 | if (ctx->gk5e == NULL) | 286 | if (ctx->gk5e == NULL) { |
287 | p = ERR_PTR(-EINVAL); | ||
286 | goto out_err; | 288 | goto out_err; |
289 | } | ||
287 | 290 | ||
288 | /* The downcall format was designed before we completely understood | 291 | /* The downcall format was designed before we completely understood |
289 | * the uses of the context fields; so it includes some stuff we | 292 | * the uses of the context fields; so it includes some stuff we |
290 | * just give some minimal sanity-checking, and some we ignore | 293 | * just give some minimal sanity-checking, and some we ignore |
291 | * completely (like the next twenty bytes): */ | 294 | * completely (like the next twenty bytes): */ |
292 | if (unlikely(p + 20 > end || p + 20 < p)) | 295 | if (unlikely(p + 20 > end || p + 20 < p)) { |
296 | p = ERR_PTR(-EFAULT); | ||
293 | goto out_err; | 297 | goto out_err; |
298 | } | ||
294 | p += 20; | 299 | p += 20; |
295 | p = simple_get_bytes(p, end, &tmp, sizeof(tmp)); | 300 | p = simple_get_bytes(p, end, &tmp, sizeof(tmp)); |
296 | if (IS_ERR(p)) | 301 | if (IS_ERR(p)) |
@@ -619,6 +624,7 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx, | |||
619 | if (ctx->seq_send64 != ctx->seq_send) { | 624 | if (ctx->seq_send64 != ctx->seq_send) { |
620 | dprintk("%s: seq_send64 %lx, seq_send %x overflow?\n", __func__, | 625 | dprintk("%s: seq_send64 %lx, seq_send %x overflow?\n", __func__, |
621 | (long unsigned)ctx->seq_send64, ctx->seq_send); | 626 | (long unsigned)ctx->seq_send64, ctx->seq_send); |
627 | p = ERR_PTR(-EINVAL); | ||
622 | goto out_err; | 628 | goto out_err; |
623 | } | 629 | } |
624 | p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype)); | 630 | p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype)); |
diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c index dc3f1f5ed865..adade3d313f2 100644 --- a/net/sunrpc/auth_gss/gss_spkm3_mech.c +++ b/net/sunrpc/auth_gss/gss_spkm3_mech.c | |||
@@ -100,6 +100,7 @@ gss_import_sec_context_spkm3(const void *p, size_t len, | |||
100 | if (version != 1) { | 100 | if (version != 1) { |
101 | dprintk("RPC: unknown spkm3 token format: " | 101 | dprintk("RPC: unknown spkm3 token format: " |
102 | "obsolete nfs-utils?\n"); | 102 | "obsolete nfs-utils?\n"); |
103 | p = ERR_PTR(-EINVAL); | ||
103 | goto out_err_free_ctx; | 104 | goto out_err_free_ctx; |
104 | } | 105 | } |
105 | 106 | ||
@@ -135,8 +136,10 @@ gss_import_sec_context_spkm3(const void *p, size_t len, | |||
135 | if (IS_ERR(p)) | 136 | if (IS_ERR(p)) |
136 | goto out_err_free_intg_alg; | 137 | goto out_err_free_intg_alg; |
137 | 138 | ||
138 | if (p != end) | 139 | if (p != end) { |
140 | p = ERR_PTR(-EFAULT); | ||
139 | goto out_err_free_intg_key; | 141 | goto out_err_free_intg_key; |
142 | } | ||
140 | 143 | ||
141 | ctx_id->internal_ctx_id = ctx; | 144 | ctx_id->internal_ctx_id = ctx; |
142 | 145 | ||
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 2388d83b68ff..fa5549079d79 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -226,7 +226,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru | |||
226 | goto out_no_principal; | 226 | goto out_no_principal; |
227 | } | 227 | } |
228 | 228 | ||
229 | kref_init(&clnt->cl_kref); | 229 | atomic_set(&clnt->cl_count, 1); |
230 | 230 | ||
231 | err = rpc_setup_pipedir(clnt, program->pipe_dir_name); | 231 | err = rpc_setup_pipedir(clnt, program->pipe_dir_name); |
232 | if (err < 0) | 232 | if (err < 0) |
@@ -390,14 +390,14 @@ rpc_clone_client(struct rpc_clnt *clnt) | |||
390 | if (new->cl_principal == NULL) | 390 | if (new->cl_principal == NULL) |
391 | goto out_no_principal; | 391 | goto out_no_principal; |
392 | } | 392 | } |
393 | kref_init(&new->cl_kref); | 393 | atomic_set(&new->cl_count, 1); |
394 | err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); | 394 | err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); |
395 | if (err != 0) | 395 | if (err != 0) |
396 | goto out_no_path; | 396 | goto out_no_path; |
397 | if (new->cl_auth) | 397 | if (new->cl_auth) |
398 | atomic_inc(&new->cl_auth->au_count); | 398 | atomic_inc(&new->cl_auth->au_count); |
399 | xprt_get(clnt->cl_xprt); | 399 | xprt_get(clnt->cl_xprt); |
400 | kref_get(&clnt->cl_kref); | 400 | atomic_inc(&clnt->cl_count); |
401 | rpc_register_client(new); | 401 | rpc_register_client(new); |
402 | rpciod_up(); | 402 | rpciod_up(); |
403 | return new; | 403 | return new; |
@@ -465,10 +465,8 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client); | |||
465 | * Free an RPC client | 465 | * Free an RPC client |
466 | */ | 466 | */ |
467 | static void | 467 | static void |
468 | rpc_free_client(struct kref *kref) | 468 | rpc_free_client(struct rpc_clnt *clnt) |
469 | { | 469 | { |
470 | struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref); | ||
471 | |||
472 | dprintk("RPC: destroying %s client for %s\n", | 470 | dprintk("RPC: destroying %s client for %s\n", |
473 | clnt->cl_protname, clnt->cl_server); | 471 | clnt->cl_protname, clnt->cl_server); |
474 | if (!IS_ERR(clnt->cl_path.dentry)) { | 472 | if (!IS_ERR(clnt->cl_path.dentry)) { |
@@ -495,12 +493,10 @@ out_free: | |||
495 | * Free an RPC client | 493 | * Free an RPC client |
496 | */ | 494 | */ |
497 | static void | 495 | static void |
498 | rpc_free_auth(struct kref *kref) | 496 | rpc_free_auth(struct rpc_clnt *clnt) |
499 | { | 497 | { |
500 | struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref); | ||
501 | |||
502 | if (clnt->cl_auth == NULL) { | 498 | if (clnt->cl_auth == NULL) { |
503 | rpc_free_client(kref); | 499 | rpc_free_client(clnt); |
504 | return; | 500 | return; |
505 | } | 501 | } |
506 | 502 | ||
@@ -509,10 +505,11 @@ rpc_free_auth(struct kref *kref) | |||
509 | * release remaining GSS contexts. This mechanism ensures | 505 | * release remaining GSS contexts. This mechanism ensures |
510 | * that it can do so safely. | 506 | * that it can do so safely. |
511 | */ | 507 | */ |
512 | kref_init(kref); | 508 | atomic_inc(&clnt->cl_count); |
513 | rpcauth_release(clnt->cl_auth); | 509 | rpcauth_release(clnt->cl_auth); |
514 | clnt->cl_auth = NULL; | 510 | clnt->cl_auth = NULL; |
515 | kref_put(kref, rpc_free_client); | 511 | if (atomic_dec_and_test(&clnt->cl_count)) |
512 | rpc_free_client(clnt); | ||
516 | } | 513 | } |
517 | 514 | ||
518 | /* | 515 | /* |
@@ -525,7 +522,8 @@ rpc_release_client(struct rpc_clnt *clnt) | |||
525 | 522 | ||
526 | if (list_empty(&clnt->cl_tasks)) | 523 | if (list_empty(&clnt->cl_tasks)) |
527 | wake_up(&destroy_wait); | 524 | wake_up(&destroy_wait); |
528 | kref_put(&clnt->cl_kref, rpc_free_auth); | 525 | if (atomic_dec_and_test(&clnt->cl_count)) |
526 | rpc_free_auth(clnt); | ||
529 | } | 527 | } |
530 | 528 | ||
531 | /** | 529 | /** |
@@ -588,7 +586,7 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt) | |||
588 | if (clnt != NULL) { | 586 | if (clnt != NULL) { |
589 | rpc_task_release_client(task); | 587 | rpc_task_release_client(task); |
590 | task->tk_client = clnt; | 588 | task->tk_client = clnt; |
591 | kref_get(&clnt->cl_kref); | 589 | atomic_inc(&clnt->cl_count); |
592 | if (clnt->cl_softrtry) | 590 | if (clnt->cl_softrtry) |
593 | task->tk_flags |= RPC_TASK_SOFT; | 591 | task->tk_flags |= RPC_TASK_SOFT; |
594 | /* Add to the client's list of all tasks */ | 592 | /* Add to the client's list of all tasks */ |
@@ -931,7 +929,7 @@ call_reserveresult(struct rpc_task *task) | |||
931 | task->tk_status = 0; | 929 | task->tk_status = 0; |
932 | if (status >= 0) { | 930 | if (status >= 0) { |
933 | if (task->tk_rqstp) { | 931 | if (task->tk_rqstp) { |
934 | task->tk_action = call_allocate; | 932 | task->tk_action = call_refresh; |
935 | return; | 933 | return; |
936 | } | 934 | } |
937 | 935 | ||
@@ -966,13 +964,54 @@ call_reserveresult(struct rpc_task *task) | |||
966 | } | 964 | } |
967 | 965 | ||
968 | /* | 966 | /* |
969 | * 2. Allocate the buffer. For details, see sched.c:rpc_malloc. | 967 | * 2. Bind and/or refresh the credentials |
968 | */ | ||
969 | static void | ||
970 | call_refresh(struct rpc_task *task) | ||
971 | { | ||
972 | dprint_status(task); | ||
973 | |||
974 | task->tk_action = call_refreshresult; | ||
975 | task->tk_status = 0; | ||
976 | task->tk_client->cl_stats->rpcauthrefresh++; | ||
977 | rpcauth_refreshcred(task); | ||
978 | } | ||
979 | |||
980 | /* | ||
981 | * 2a. Process the results of a credential refresh | ||
982 | */ | ||
983 | static void | ||
984 | call_refreshresult(struct rpc_task *task) | ||
985 | { | ||
986 | int status = task->tk_status; | ||
987 | |||
988 | dprint_status(task); | ||
989 | |||
990 | task->tk_status = 0; | ||
991 | task->tk_action = call_allocate; | ||
992 | if (status >= 0 && rpcauth_uptodatecred(task)) | ||
993 | return; | ||
994 | switch (status) { | ||
995 | case -EACCES: | ||
996 | rpc_exit(task, -EACCES); | ||
997 | return; | ||
998 | case -ENOMEM: | ||
999 | rpc_exit(task, -ENOMEM); | ||
1000 | return; | ||
1001 | case -ETIMEDOUT: | ||
1002 | rpc_delay(task, 3*HZ); | ||
1003 | } | ||
1004 | task->tk_action = call_refresh; | ||
1005 | } | ||
1006 | |||
1007 | /* | ||
1008 | * 2b. Allocate the buffer. For details, see sched.c:rpc_malloc. | ||
970 | * (Note: buffer memory is freed in xprt_release). | 1009 | * (Note: buffer memory is freed in xprt_release). |
971 | */ | 1010 | */ |
972 | static void | 1011 | static void |
973 | call_allocate(struct rpc_task *task) | 1012 | call_allocate(struct rpc_task *task) |
974 | { | 1013 | { |
975 | unsigned int slack = task->tk_client->cl_auth->au_cslack; | 1014 | unsigned int slack = task->tk_rqstp->rq_cred->cr_auth->au_cslack; |
976 | struct rpc_rqst *req = task->tk_rqstp; | 1015 | struct rpc_rqst *req = task->tk_rqstp; |
977 | struct rpc_xprt *xprt = task->tk_xprt; | 1016 | struct rpc_xprt *xprt = task->tk_xprt; |
978 | struct rpc_procinfo *proc = task->tk_msg.rpc_proc; | 1017 | struct rpc_procinfo *proc = task->tk_msg.rpc_proc; |
@@ -980,7 +1019,7 @@ call_allocate(struct rpc_task *task) | |||
980 | dprint_status(task); | 1019 | dprint_status(task); |
981 | 1020 | ||
982 | task->tk_status = 0; | 1021 | task->tk_status = 0; |
983 | task->tk_action = call_refresh; | 1022 | task->tk_action = call_bind; |
984 | 1023 | ||
985 | if (req->rq_buffer) | 1024 | if (req->rq_buffer) |
986 | return; | 1025 | return; |
@@ -1017,47 +1056,6 @@ call_allocate(struct rpc_task *task) | |||
1017 | rpc_exit(task, -ERESTARTSYS); | 1056 | rpc_exit(task, -ERESTARTSYS); |
1018 | } | 1057 | } |
1019 | 1058 | ||
1020 | /* | ||
1021 | * 2a. Bind and/or refresh the credentials | ||
1022 | */ | ||
1023 | static void | ||
1024 | call_refresh(struct rpc_task *task) | ||
1025 | { | ||
1026 | dprint_status(task); | ||
1027 | |||
1028 | task->tk_action = call_refreshresult; | ||
1029 | task->tk_status = 0; | ||
1030 | task->tk_client->cl_stats->rpcauthrefresh++; | ||
1031 | rpcauth_refreshcred(task); | ||
1032 | } | ||
1033 | |||
1034 | /* | ||
1035 | * 2b. Process the results of a credential refresh | ||
1036 | */ | ||
1037 | static void | ||
1038 | call_refreshresult(struct rpc_task *task) | ||
1039 | { | ||
1040 | int status = task->tk_status; | ||
1041 | |||
1042 | dprint_status(task); | ||
1043 | |||
1044 | task->tk_status = 0; | ||
1045 | task->tk_action = call_bind; | ||
1046 | if (status >= 0 && rpcauth_uptodatecred(task)) | ||
1047 | return; | ||
1048 | switch (status) { | ||
1049 | case -EACCES: | ||
1050 | rpc_exit(task, -EACCES); | ||
1051 | return; | ||
1052 | case -ENOMEM: | ||
1053 | rpc_exit(task, -ENOMEM); | ||
1054 | return; | ||
1055 | case -ETIMEDOUT: | ||
1056 | rpc_delay(task, 3*HZ); | ||
1057 | } | ||
1058 | task->tk_action = call_refresh; | ||
1059 | } | ||
1060 | |||
1061 | static inline int | 1059 | static inline int |
1062 | rpc_task_need_encode(struct rpc_task *task) | 1060 | rpc_task_need_encode(struct rpc_task *task) |
1063 | { | 1061 | { |
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 95ccbcf45d3e..8c8eef2b8f26 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c | |||
@@ -48,7 +48,7 @@ static void rpc_purge_list(struct rpc_inode *rpci, struct list_head *head, | |||
48 | return; | 48 | return; |
49 | do { | 49 | do { |
50 | msg = list_entry(head->next, struct rpc_pipe_msg, list); | 50 | msg = list_entry(head->next, struct rpc_pipe_msg, list); |
51 | list_del(&msg->list); | 51 | list_del_init(&msg->list); |
52 | msg->errno = err; | 52 | msg->errno = err; |
53 | destroy_msg(msg); | 53 | destroy_msg(msg); |
54 | } while (!list_empty(head)); | 54 | } while (!list_empty(head)); |
@@ -208,7 +208,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) | |||
208 | if (msg != NULL) { | 208 | if (msg != NULL) { |
209 | spin_lock(&inode->i_lock); | 209 | spin_lock(&inode->i_lock); |
210 | msg->errno = -EAGAIN; | 210 | msg->errno = -EAGAIN; |
211 | list_del(&msg->list); | 211 | list_del_init(&msg->list); |
212 | spin_unlock(&inode->i_lock); | 212 | spin_unlock(&inode->i_lock); |
213 | rpci->ops->destroy_msg(msg); | 213 | rpci->ops->destroy_msg(msg); |
214 | } | 214 | } |
@@ -268,7 +268,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) | |||
268 | if (res < 0 || msg->len == msg->copied) { | 268 | if (res < 0 || msg->len == msg->copied) { |
269 | filp->private_data = NULL; | 269 | filp->private_data = NULL; |
270 | spin_lock(&inode->i_lock); | 270 | spin_lock(&inode->i_lock); |
271 | list_del(&msg->list); | 271 | list_del_init(&msg->list); |
272 | spin_unlock(&inode->i_lock); | 272 | spin_unlock(&inode->i_lock); |
273 | rpci->ops->destroy_msg(msg); | 273 | rpci->ops->destroy_msg(msg); |
274 | } | 274 | } |
@@ -371,21 +371,23 @@ rpc_show_info(struct seq_file *m, void *v) | |||
371 | static int | 371 | static int |
372 | rpc_info_open(struct inode *inode, struct file *file) | 372 | rpc_info_open(struct inode *inode, struct file *file) |
373 | { | 373 | { |
374 | struct rpc_clnt *clnt; | 374 | struct rpc_clnt *clnt = NULL; |
375 | int ret = single_open(file, rpc_show_info, NULL); | 375 | int ret = single_open(file, rpc_show_info, NULL); |
376 | 376 | ||
377 | if (!ret) { | 377 | if (!ret) { |
378 | struct seq_file *m = file->private_data; | 378 | struct seq_file *m = file->private_data; |
379 | mutex_lock(&inode->i_mutex); | 379 | |
380 | clnt = RPC_I(inode)->private; | 380 | spin_lock(&file->f_path.dentry->d_lock); |
381 | if (clnt) { | 381 | if (!d_unhashed(file->f_path.dentry)) |
382 | kref_get(&clnt->cl_kref); | 382 | clnt = RPC_I(inode)->private; |
383 | if (clnt != NULL && atomic_inc_not_zero(&clnt->cl_count)) { | ||
384 | spin_unlock(&file->f_path.dentry->d_lock); | ||
383 | m->private = clnt; | 385 | m->private = clnt; |
384 | } else { | 386 | } else { |
387 | spin_unlock(&file->f_path.dentry->d_lock); | ||
385 | single_release(inode, file); | 388 | single_release(inode, file); |
386 | ret = -EINVAL; | 389 | ret = -EINVAL; |
387 | } | 390 | } |
388 | mutex_unlock(&inode->i_mutex); | ||
389 | } | 391 | } |
390 | return ret; | 392 | return ret; |
391 | } | 393 | } |