diff options
-rw-r--r-- | fs/dcache.c | 45 | ||||
-rw-r--r-- | fs/namei.c | 22 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 |
3 files changed, 34 insertions, 34 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index c6fd1f27da57..64024005da43 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1720,18 +1720,23 @@ void d_move(struct dentry * dentry, struct dentry * target) | |||
1720 | spin_unlock(&dcache_lock); | 1720 | spin_unlock(&dcache_lock); |
1721 | } | 1721 | } |
1722 | 1722 | ||
1723 | /* | 1723 | /** |
1724 | * Helper that returns 1 if p1 is a parent of p2, else 0 | 1724 | * d_ancestor - search for an ancestor |
1725 | * @p1: ancestor dentry | ||
1726 | * @p2: child dentry | ||
1727 | * | ||
1728 | * Returns the ancestor dentry of p2 which is a child of p1, if p1 is | ||
1729 | * an ancestor of p2, else NULL. | ||
1725 | */ | 1730 | */ |
1726 | static int d_isparent(struct dentry *p1, struct dentry *p2) | 1731 | struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2) |
1727 | { | 1732 | { |
1728 | struct dentry *p; | 1733 | struct dentry *p; |
1729 | 1734 | ||
1730 | for (p = p2; !IS_ROOT(p); p = p->d_parent) { | 1735 | for (p = p2; !IS_ROOT(p); p = p->d_parent) { |
1731 | if (p->d_parent == p1) | 1736 | if (p->d_parent == p1) |
1732 | return 1; | 1737 | return p; |
1733 | } | 1738 | } |
1734 | return 0; | 1739 | return NULL; |
1735 | } | 1740 | } |
1736 | 1741 | ||
1737 | /* | 1742 | /* |
@@ -1755,7 +1760,7 @@ static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias) | |||
1755 | 1760 | ||
1756 | /* Check for loops */ | 1761 | /* Check for loops */ |
1757 | ret = ERR_PTR(-ELOOP); | 1762 | ret = ERR_PTR(-ELOOP); |
1758 | if (d_isparent(alias, dentry)) | 1763 | if (d_ancestor(alias, dentry)) |
1759 | goto out_err; | 1764 | goto out_err; |
1760 | 1765 | ||
1761 | /* See lock_rename() */ | 1766 | /* See lock_rename() */ |
@@ -2155,31 +2160,27 @@ out: | |||
2155 | * Caller must ensure that "new_dentry" is pinned before calling is_subdir() | 2160 | * Caller must ensure that "new_dentry" is pinned before calling is_subdir() |
2156 | */ | 2161 | */ |
2157 | 2162 | ||
2158 | int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) | 2163 | int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) |
2159 | { | 2164 | { |
2160 | int result; | 2165 | int result; |
2161 | struct dentry * saved = new_dentry; | ||
2162 | unsigned long seq; | 2166 | unsigned long seq; |
2163 | 2167 | ||
2164 | /* need rcu_readlock to protect against the d_parent trashing due to | 2168 | /* FIXME: This is old behavior, needed? Please check callers. */ |
2165 | * d_move | 2169 | if (new_dentry == old_dentry) |
2170 | return 1; | ||
2171 | |||
2172 | /* | ||
2173 | * Need rcu_readlock to protect against the d_parent trashing | ||
2174 | * due to d_move | ||
2166 | */ | 2175 | */ |
2167 | rcu_read_lock(); | 2176 | rcu_read_lock(); |
2168 | do { | 2177 | do { |
2169 | /* for restarting inner loop in case of seq retry */ | 2178 | /* for restarting inner loop in case of seq retry */ |
2170 | new_dentry = saved; | ||
2171 | result = 0; | ||
2172 | seq = read_seqbegin(&rename_lock); | 2179 | seq = read_seqbegin(&rename_lock); |
2173 | for (;;) { | 2180 | if (d_ancestor(old_dentry, new_dentry)) |
2174 | if (new_dentry != old_dentry) { | ||
2175 | if (IS_ROOT(new_dentry)) | ||
2176 | break; | ||
2177 | new_dentry = new_dentry->d_parent; | ||
2178 | continue; | ||
2179 | } | ||
2180 | result = 1; | 2181 | result = 1; |
2181 | break; | 2182 | else |
2182 | } | 2183 | result = 0; |
2183 | } while (read_seqretry(&rename_lock, seq)); | 2184 | } while (read_seqretry(&rename_lock, seq)); |
2184 | rcu_read_unlock(); | 2185 | rcu_read_unlock(); |
2185 | 2186 | ||
diff --git a/fs/namei.c b/fs/namei.c index 068a9e50c8c0..b7cd65224d60 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1454,20 +1454,18 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) | |||
1454 | 1454 | ||
1455 | mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); | 1455 | mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); |
1456 | 1456 | ||
1457 | for (p = p1; !IS_ROOT(p); p = p->d_parent) { | 1457 | p = d_ancestor(p2, p1); |
1458 | if (p->d_parent == p2) { | 1458 | if (p) { |
1459 | mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); | 1459 | mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); |
1460 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); | 1460 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); |
1461 | return p; | 1461 | return p; |
1462 | } | ||
1463 | } | 1462 | } |
1464 | 1463 | ||
1465 | for (p = p2; !IS_ROOT(p); p = p->d_parent) { | 1464 | p = d_ancestor(p1, p2); |
1466 | if (p->d_parent == p1) { | 1465 | if (p) { |
1467 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); | 1466 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); |
1468 | mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); | 1467 | mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); |
1469 | return p; | 1468 | return p; |
1470 | } | ||
1471 | } | 1469 | } |
1472 | 1470 | ||
1473 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); | 1471 | mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 74c64ae30cf0..a37359d0bad1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -287,6 +287,7 @@ static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *in | |||
287 | 287 | ||
288 | /* used for rename() and baskets */ | 288 | /* used for rename() and baskets */ |
289 | extern void d_move(struct dentry *, struct dentry *); | 289 | extern void d_move(struct dentry *, struct dentry *); |
290 | extern struct dentry *d_ancestor(struct dentry *, struct dentry *); | ||
290 | 291 | ||
291 | /* appendix may either be NULL or be used for transname suffixes */ | 292 | /* appendix may either be NULL or be used for transname suffixes */ |
292 | extern struct dentry * d_lookup(struct dentry *, struct qstr *); | 293 | extern struct dentry * d_lookup(struct dentry *, struct qstr *); |