Loading fs/dcache.c +35 −108 Original line number Original line Diff line number Diff line Loading @@ -2575,11 +2575,11 @@ struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2) * Note: If ever the locking in lock_rename() changes, then please * Note: If ever the locking in lock_rename() changes, then please * remember to update this too... * remember to update this too... */ */ static struct dentry *__d_unalias(struct inode *inode, static int __d_unalias(struct inode *inode, struct dentry *dentry, struct dentry *alias) struct dentry *dentry, struct dentry *alias) { { struct mutex *m1 = NULL, *m2 = NULL; struct mutex *m1 = NULL, *m2 = NULL; struct dentry *ret = ERR_PTR(-EBUSY); int ret = -EBUSY; /* If alias and dentry share a parent, then no extra locks required */ /* If alias and dentry share a parent, then no extra locks required */ if (alias->d_parent == dentry->d_parent) if (alias->d_parent == dentry->d_parent) Loading @@ -2594,7 +2594,7 @@ static struct dentry *__d_unalias(struct inode *inode, m2 = &alias->d_parent->d_inode->i_mutex; m2 = &alias->d_parent->d_inode->i_mutex; out_unalias: out_unalias: __d_move(alias, dentry, false); __d_move(alias, dentry, false); ret = alias; ret = 0; out_err: out_err: spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock); if (m2) if (m2) Loading Loading @@ -2629,130 +2629,57 @@ static struct dentry *__d_unalias(struct inode *inode, */ */ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) { { struct dentry *new = NULL; if (IS_ERR(inode)) if (IS_ERR(inode)) return ERR_CAST(inode); return ERR_CAST(inode); if (inode && S_ISDIR(inode->i_mode)) { spin_lock(&inode->i_lock); new = __d_find_any_alias(inode); if (new) { if (!IS_ROOT(new)) { spin_unlock(&inode->i_lock); dput(new); iput(inode); return ERR_PTR(-EIO); } if (d_ancestor(new, dentry)) { spin_unlock(&inode->i_lock); dput(new); iput(inode); return ERR_PTR(-EIO); } write_seqlock(&rename_lock); __d_move(new, dentry, false); write_sequnlock(&rename_lock); spin_unlock(&inode->i_lock); security_d_instantiate(new, inode); iput(inode); } else { /* already taking inode->i_lock, so d_add() by hand */ __d_instantiate(dentry, inode); spin_unlock(&inode->i_lock); security_d_instantiate(dentry, inode); d_rehash(dentry); } } else { d_instantiate(dentry, inode); if (d_unhashed(dentry)) d_rehash(dentry); } return new; } EXPORT_SYMBOL(d_splice_alias); /** * d_materialise_unique - introduce an inode into the tree * @dentry: candidate dentry * @inode: inode to bind to the dentry, to which aliases may be attached * * Introduces an dentry into the tree, substituting an extant disconnected * root directory alias in its place if there is one. Caller must hold the * i_mutex of the parent directory. */ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) { struct dentry *actual; BUG_ON(!d_unhashed(dentry)); BUG_ON(!d_unhashed(dentry)); if (!inode) { if (!inode) { actual = dentry; __d_instantiate(dentry, NULL); __d_instantiate(dentry, NULL); d_rehash(actual); goto out; goto out_nolock; } } spin_lock(&inode->i_lock); spin_lock(&inode->i_lock); if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) { struct dentry *alias; struct dentry *new = __d_find_any_alias(inode); if (unlikely(new)) { /* Does an aliased dentry already exist? */ alias = __d_find_alias(inode); if (alias) { actual = alias; write_seqlock(&rename_lock); write_seqlock(&rename_lock); if (unlikely(d_ancestor(new, dentry))) { if (d_ancestor(alias, dentry)) { /* Check for loops */ actual = ERR_PTR(-ELOOP); spin_unlock(&inode->i_lock); } else if (IS_ROOT(alias)) { /* Is this an anonymous mountpoint that we * could splice into our tree? */ __d_move(alias, dentry, false); write_sequnlock(&rename_lock); write_sequnlock(&rename_lock); goto found; spin_unlock(&inode->i_lock); } else { dput(new); /* Nope, but we must(!) avoid directory new = ERR_PTR(-ELOOP); * aliasing. This drops inode->i_lock */ actual = __d_unalias(inode, dentry, alias); } write_sequnlock(&rename_lock); if (IS_ERR(actual)) { if (PTR_ERR(actual) == -ELOOP) pr_warn_ratelimited( pr_warn_ratelimited( "VFS: Lookup of '%s' in %s %s" "VFS: Lookup of '%s' in %s %s" " would have caused loop\n", " would have caused loop\n", dentry->d_name.name, dentry->d_name.name, inode->i_sb->s_type->name, inode->i_sb->s_type->name, inode->i_sb->s_id); inode->i_sb->s_id); dput(alias); } else if (!IS_ROOT(new)) { int err = __d_unalias(inode, dentry, new); write_sequnlock(&rename_lock); if (err) { dput(new); new = ERR_PTR(err); } } goto out_nolock; } else { __d_move(new, dentry, false); write_sequnlock(&rename_lock); spin_unlock(&inode->i_lock); security_d_instantiate(new, inode); } } iput(inode); return new; } } } /* Add a unique reference */ /* already taking inode->i_lock, so d_add() by hand */ actual = __d_instantiate_unique(dentry, inode); __d_instantiate(dentry, inode); if (!actual) actual = dentry; d_rehash(actual); found: spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock); out_nolock: out: if (actual == dentry) { security_d_instantiate(dentry, inode); security_d_instantiate(dentry, inode); d_rehash(dentry); return NULL; return NULL; } } EXPORT_SYMBOL(d_splice_alias); iput(inode); return actual; } EXPORT_SYMBOL_GPL(d_materialise_unique); static int prepend(char **buffer, int *buflen, const char *str, int namelen) static int prepend(char **buffer, int *buflen, const char *str, int namelen) { { Loading include/linux/dcache.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -230,7 +230,7 @@ extern seqlock_t rename_lock; */ */ extern void d_instantiate(struct dentry *, struct inode *); extern void d_instantiate(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_materialise_unique(struct dentry *, struct inode *); #define d_materialise_unique(d, i) d_splice_alias(i, d) extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern void __d_drop(struct dentry *dentry); extern void __d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); Loading Loading
fs/dcache.c +35 −108 Original line number Original line Diff line number Diff line Loading @@ -2575,11 +2575,11 @@ struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2) * Note: If ever the locking in lock_rename() changes, then please * Note: If ever the locking in lock_rename() changes, then please * remember to update this too... * remember to update this too... */ */ static struct dentry *__d_unalias(struct inode *inode, static int __d_unalias(struct inode *inode, struct dentry *dentry, struct dentry *alias) struct dentry *dentry, struct dentry *alias) { { struct mutex *m1 = NULL, *m2 = NULL; struct mutex *m1 = NULL, *m2 = NULL; struct dentry *ret = ERR_PTR(-EBUSY); int ret = -EBUSY; /* If alias and dentry share a parent, then no extra locks required */ /* If alias and dentry share a parent, then no extra locks required */ if (alias->d_parent == dentry->d_parent) if (alias->d_parent == dentry->d_parent) Loading @@ -2594,7 +2594,7 @@ static struct dentry *__d_unalias(struct inode *inode, m2 = &alias->d_parent->d_inode->i_mutex; m2 = &alias->d_parent->d_inode->i_mutex; out_unalias: out_unalias: __d_move(alias, dentry, false); __d_move(alias, dentry, false); ret = alias; ret = 0; out_err: out_err: spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock); if (m2) if (m2) Loading Loading @@ -2629,130 +2629,57 @@ static struct dentry *__d_unalias(struct inode *inode, */ */ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) { { struct dentry *new = NULL; if (IS_ERR(inode)) if (IS_ERR(inode)) return ERR_CAST(inode); return ERR_CAST(inode); if (inode && S_ISDIR(inode->i_mode)) { spin_lock(&inode->i_lock); new = __d_find_any_alias(inode); if (new) { if (!IS_ROOT(new)) { spin_unlock(&inode->i_lock); dput(new); iput(inode); return ERR_PTR(-EIO); } if (d_ancestor(new, dentry)) { spin_unlock(&inode->i_lock); dput(new); iput(inode); return ERR_PTR(-EIO); } write_seqlock(&rename_lock); __d_move(new, dentry, false); write_sequnlock(&rename_lock); spin_unlock(&inode->i_lock); security_d_instantiate(new, inode); iput(inode); } else { /* already taking inode->i_lock, so d_add() by hand */ __d_instantiate(dentry, inode); spin_unlock(&inode->i_lock); security_d_instantiate(dentry, inode); d_rehash(dentry); } } else { d_instantiate(dentry, inode); if (d_unhashed(dentry)) d_rehash(dentry); } return new; } EXPORT_SYMBOL(d_splice_alias); /** * d_materialise_unique - introduce an inode into the tree * @dentry: candidate dentry * @inode: inode to bind to the dentry, to which aliases may be attached * * Introduces an dentry into the tree, substituting an extant disconnected * root directory alias in its place if there is one. Caller must hold the * i_mutex of the parent directory. */ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) { struct dentry *actual; BUG_ON(!d_unhashed(dentry)); BUG_ON(!d_unhashed(dentry)); if (!inode) { if (!inode) { actual = dentry; __d_instantiate(dentry, NULL); __d_instantiate(dentry, NULL); d_rehash(actual); goto out; goto out_nolock; } } spin_lock(&inode->i_lock); spin_lock(&inode->i_lock); if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) { struct dentry *alias; struct dentry *new = __d_find_any_alias(inode); if (unlikely(new)) { /* Does an aliased dentry already exist? */ alias = __d_find_alias(inode); if (alias) { actual = alias; write_seqlock(&rename_lock); write_seqlock(&rename_lock); if (unlikely(d_ancestor(new, dentry))) { if (d_ancestor(alias, dentry)) { /* Check for loops */ actual = ERR_PTR(-ELOOP); spin_unlock(&inode->i_lock); } else if (IS_ROOT(alias)) { /* Is this an anonymous mountpoint that we * could splice into our tree? */ __d_move(alias, dentry, false); write_sequnlock(&rename_lock); write_sequnlock(&rename_lock); goto found; spin_unlock(&inode->i_lock); } else { dput(new); /* Nope, but we must(!) avoid directory new = ERR_PTR(-ELOOP); * aliasing. This drops inode->i_lock */ actual = __d_unalias(inode, dentry, alias); } write_sequnlock(&rename_lock); if (IS_ERR(actual)) { if (PTR_ERR(actual) == -ELOOP) pr_warn_ratelimited( pr_warn_ratelimited( "VFS: Lookup of '%s' in %s %s" "VFS: Lookup of '%s' in %s %s" " would have caused loop\n", " would have caused loop\n", dentry->d_name.name, dentry->d_name.name, inode->i_sb->s_type->name, inode->i_sb->s_type->name, inode->i_sb->s_id); inode->i_sb->s_id); dput(alias); } else if (!IS_ROOT(new)) { int err = __d_unalias(inode, dentry, new); write_sequnlock(&rename_lock); if (err) { dput(new); new = ERR_PTR(err); } } goto out_nolock; } else { __d_move(new, dentry, false); write_sequnlock(&rename_lock); spin_unlock(&inode->i_lock); security_d_instantiate(new, inode); } } iput(inode); return new; } } } /* Add a unique reference */ /* already taking inode->i_lock, so d_add() by hand */ actual = __d_instantiate_unique(dentry, inode); __d_instantiate(dentry, inode); if (!actual) actual = dentry; d_rehash(actual); found: spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock); out_nolock: out: if (actual == dentry) { security_d_instantiate(dentry, inode); security_d_instantiate(dentry, inode); d_rehash(dentry); return NULL; return NULL; } } EXPORT_SYMBOL(d_splice_alias); iput(inode); return actual; } EXPORT_SYMBOL_GPL(d_materialise_unique); static int prepend(char **buffer, int *buflen, const char *str, int namelen) static int prepend(char **buffer, int *buflen, const char *str, int namelen) { { Loading
include/linux/dcache.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -230,7 +230,7 @@ extern seqlock_t rename_lock; */ */ extern void d_instantiate(struct dentry *, struct inode *); extern void d_instantiate(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_materialise_unique(struct dentry *, struct inode *); #define d_materialise_unique(d, i) d_splice_alias(i, d) extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern void __d_drop(struct dentry *dentry); extern void __d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); Loading