diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-02-15 01:32:55 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-02-15 02:26:54 -0500 |
commit | f5e1c1c1afc1d979e2ac6a24cc99ba7143639f4d (patch) | |
tree | 48dad00e6bead517191094c0c05ef7b01c226e25 /fs/namei.c | |
parent | 24643087e748bf192f1182766716e522dc1c972f (diff) |
split do_revalidate() into RCU and non-RCU cases
fixing oopsen in lookup_one_len()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/fs/namei.c b/fs/namei.c index 7609bacc7046..a98f7f141780 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -592,12 +592,10 @@ static int d_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
592 | return status; | 592 | return status; |
593 | } | 593 | } |
594 | 594 | ||
595 | static inline struct dentry * | 595 | static struct dentry * |
596 | do_revalidate(struct dentry *dentry, struct nameidata *nd) | 596 | do_revalidate(struct dentry *dentry, struct nameidata *nd) |
597 | { | 597 | { |
598 | int status; | 598 | int status = d_revalidate(dentry, nd); |
599 | |||
600 | status = d_revalidate(dentry, nd); | ||
601 | if (unlikely(status <= 0)) { | 599 | if (unlikely(status <= 0)) { |
602 | /* | 600 | /* |
603 | * The dentry failed validation. | 601 | * The dentry failed validation. |
@@ -606,24 +604,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
606 | * to return a fail status. | 604 | * to return a fail status. |
607 | */ | 605 | */ |
608 | if (status < 0) { | 606 | if (status < 0) { |
609 | /* If we're in rcu-walk, we don't have a ref */ | 607 | dput(dentry); |
610 | if (!(nd->flags & LOOKUP_RCU)) | ||
611 | dput(dentry); | ||
612 | dentry = ERR_PTR(status); | 608 | dentry = ERR_PTR(status); |
613 | 609 | } else if (!d_invalidate(dentry)) { | |
614 | } else { | 610 | dput(dentry); |
615 | /* Don't d_invalidate in rcu-walk mode */ | 611 | dentry = NULL; |
616 | if (nameidata_dentry_drop_rcu_maybe(nd, dentry)) | ||
617 | return ERR_PTR(-ECHILD); | ||
618 | if (!d_invalidate(dentry)) { | ||
619 | dput(dentry); | ||
620 | dentry = NULL; | ||
621 | } | ||
622 | } | 612 | } |
623 | } | 613 | } |
624 | return dentry; | 614 | return dentry; |
625 | } | 615 | } |
626 | 616 | ||
617 | static inline struct dentry * | ||
618 | do_revalidate_rcu(struct dentry *dentry, struct nameidata *nd) | ||
619 | { | ||
620 | int status = dentry->d_op->d_revalidate(dentry, nd); | ||
621 | if (likely(status > 0)) | ||
622 | return dentry; | ||
623 | if (status == -ECHILD) { | ||
624 | if (nameidata_dentry_drop_rcu(nd, dentry)) | ||
625 | return ERR_PTR(-ECHILD); | ||
626 | return do_revalidate(dentry, nd); | ||
627 | } | ||
628 | if (status < 0) | ||
629 | return ERR_PTR(status); | ||
630 | /* Don't d_invalidate in rcu-walk mode */ | ||
631 | if (nameidata_dentry_drop_rcu(nd, dentry)) | ||
632 | return ERR_PTR(-ECHILD); | ||
633 | if (!d_invalidate(dentry)) { | ||
634 | dput(dentry); | ||
635 | dentry = NULL; | ||
636 | } | ||
637 | return dentry; | ||
638 | } | ||
639 | |||
627 | static inline int need_reval_dot(struct dentry *dentry) | 640 | static inline int need_reval_dot(struct dentry *dentry) |
628 | { | 641 | { |
629 | if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE))) | 642 | if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE))) |
@@ -1260,7 +1273,7 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, | |||
1260 | 1273 | ||
1261 | nd->seq = seq; | 1274 | nd->seq = seq; |
1262 | if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) { | 1275 | if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) { |
1263 | dentry = do_revalidate(dentry, nd); | 1276 | dentry = do_revalidate_rcu(dentry, nd); |
1264 | if (!dentry) | 1277 | if (!dentry) |
1265 | goto need_lookup; | 1278 | goto need_lookup; |
1266 | if (IS_ERR(dentry)) | 1279 | if (IS_ERR(dentry)) |