Loading fs/dcache.c +35 −108 Original line number 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 * 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 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->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; out_unalias: __d_move(alias, dentry, false); ret = alias; ret = 0; out_err: spin_unlock(&inode->i_lock); 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 *new = NULL; if (IS_ERR(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)); if (!inode) { actual = dentry; __d_instantiate(dentry, NULL); d_rehash(actual); goto out_nolock; goto out; } spin_lock(&inode->i_lock); if (S_ISDIR(inode->i_mode)) { struct dentry *alias; /* Does an aliased dentry already exist? */ alias = __d_find_alias(inode); if (alias) { actual = alias; struct dentry *new = __d_find_any_alias(inode); if (unlikely(new)) { write_seqlock(&rename_lock); 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); if (unlikely(d_ancestor(new, dentry))) { write_sequnlock(&rename_lock); goto found; } else { /* Nope, but we must(!) avoid directory * 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) spin_unlock(&inode->i_lock); dput(new); new = ERR_PTR(-ELOOP); pr_warn_ratelimited( "VFS: Lookup of '%s' in %s %s" " would have caused loop\n", dentry->d_name.name, inode->i_sb->s_type->name, 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 */ actual = __d_instantiate_unique(dentry, inode); if (!actual) actual = dentry; d_rehash(actual); found: } /* already taking inode->i_lock, so d_add() by hand */ __d_instantiate(dentry, inode); spin_unlock(&inode->i_lock); out_nolock: if (actual == dentry) { out: security_d_instantiate(dentry, inode); d_rehash(dentry); return NULL; } iput(inode); return actual; } EXPORT_SYMBOL_GPL(d_materialise_unique); EXPORT_SYMBOL(d_splice_alias); static int prepend(char **buffer, int *buflen, const char *str, int namelen) { Loading include/linux/dcache.h +1 −1 Original line number Diff line number Diff line Loading @@ -230,7 +230,7 @@ extern seqlock_t rename_lock; */ extern void d_instantiate(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 void __d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); Loading Loading
fs/dcache.c +35 −108 Original line number 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 * 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 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->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; out_unalias: __d_move(alias, dentry, false); ret = alias; ret = 0; out_err: spin_unlock(&inode->i_lock); 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 *new = NULL; if (IS_ERR(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)); if (!inode) { actual = dentry; __d_instantiate(dentry, NULL); d_rehash(actual); goto out_nolock; goto out; } spin_lock(&inode->i_lock); if (S_ISDIR(inode->i_mode)) { struct dentry *alias; /* Does an aliased dentry already exist? */ alias = __d_find_alias(inode); if (alias) { actual = alias; struct dentry *new = __d_find_any_alias(inode); if (unlikely(new)) { write_seqlock(&rename_lock); 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); if (unlikely(d_ancestor(new, dentry))) { write_sequnlock(&rename_lock); goto found; } else { /* Nope, but we must(!) avoid directory * 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) spin_unlock(&inode->i_lock); dput(new); new = ERR_PTR(-ELOOP); pr_warn_ratelimited( "VFS: Lookup of '%s' in %s %s" " would have caused loop\n", dentry->d_name.name, inode->i_sb->s_type->name, 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 */ actual = __d_instantiate_unique(dentry, inode); if (!actual) actual = dentry; d_rehash(actual); found: } /* already taking inode->i_lock, so d_add() by hand */ __d_instantiate(dentry, inode); spin_unlock(&inode->i_lock); out_nolock: if (actual == dentry) { out: security_d_instantiate(dentry, inode); d_rehash(dentry); return NULL; } iput(inode); return actual; } EXPORT_SYMBOL_GPL(d_materialise_unique); EXPORT_SYMBOL(d_splice_alias); static int prepend(char **buffer, int *buflen, const char *str, int namelen) { Loading
include/linux/dcache.h +1 −1 Original line number Diff line number Diff line Loading @@ -230,7 +230,7 @@ extern seqlock_t rename_lock; */ extern void d_instantiate(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 void __d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); Loading