Commit 73597e3e authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: ensure dentry consistency when the orphanage adopts a file

When the orphanage adopts a file, that file becomes a child of the
orphanage.  The dentry cache may have entries for the orphanage
directory and the name we've chosen, so (1) make sure we abort if the
dcache has a positive entry because something's not right; and (2)
invalidate and purge negative dentries if the adoption goes through.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent e6c9e75f
......@@ -418,6 +418,90 @@ xrep_adoption_compute_name(
return 0;
}
/*
* Make sure the dcache does not have a positive dentry for the name we've
* chosen. The caller should have checked with the ondisk directory, so any
* discrepancy is a sign that something is seriously wrong.
*/
static int
xrep_adoption_check_dcache(
struct xrep_adoption *adopt)
{
struct qstr qname = QSTR_INIT(adopt->xname->name,
adopt->xname->len);
struct dentry *d_orphanage, *d_child;
int error = 0;
d_orphanage = d_find_alias(VFS_I(adopt->sc->orphanage));
if (!d_orphanage)
return 0;
d_child = d_hash_and_lookup(d_orphanage, &qname);
if (d_child) {
trace_xrep_adoption_check_child(adopt->sc->mp, d_child);
if (d_is_positive(d_child)) {
ASSERT(d_is_negative(d_child));
error = -EFSCORRUPTED;
}
dput(d_child);
}
dput(d_orphanage);
if (error)
return error;
/*
* Do we need to update d_parent of the dentry for the file being
* repaired? There shouldn't be a hashed dentry with a parent since
* the file had nonzero nlink but wasn't connected to any parent dir.
*/
d_child = d_find_alias(VFS_I(adopt->sc->ip));
if (!d_child)
return 0;
trace_xrep_adoption_check_alias(adopt->sc->mp, d_child);
if (d_child->d_parent && !d_unhashed(d_child)) {
ASSERT(d_child->d_parent == NULL || d_unhashed(d_child));
error = -EFSCORRUPTED;
}
dput(d_child);
return error;
}
/*
* Remove all negative dentries from the dcache. There should not be any
* positive entries, since we've maintained our lock on the orphanage
* directory.
*/
static void
xrep_adoption_zap_dcache(
struct xrep_adoption *adopt)
{
struct qstr qname = QSTR_INIT(adopt->xname->name,
adopt->xname->len);
struct dentry *d_orphanage, *d_child;
d_orphanage = d_find_alias(VFS_I(adopt->sc->orphanage));
if (!d_orphanage)
return;
d_child = d_hash_and_lookup(d_orphanage, &qname);
while (d_child != NULL) {
trace_xrep_adoption_invalidate_child(adopt->sc->mp, d_child);
ASSERT(d_is_negative(d_child));
d_invalidate(d_child);
dput(d_child);
d_child = d_lookup(d_orphanage, &qname);
}
dput(d_orphanage);
}
/*
* Move the current file to the orphanage under the computed name.
*
......@@ -435,6 +519,10 @@ xrep_adoption_move(
trace_xrep_adoption_reparent(sc->orphanage, adopt->xname,
sc->ip->i_ino);
error = xrep_adoption_check_dcache(adopt);
if (error)
return error;
/* Create the new name in the orphanage. */
error = xfs_dir_createname(sc->tp, sc->orphanage, adopt->xname,
sc->ip->i_ino, adopt->orphanage_blkres);
......@@ -465,6 +553,9 @@ xrep_adoption_move(
* recorded in the log.
*/
xfs_dir_update_hook(sc->orphanage, sc->ip, 1, adopt->xname);
/* Remove negative dentries from the lost+found's dcache */
xrep_adoption_zap_dcache(adopt);
return 0;
}
......
......@@ -2669,6 +2669,48 @@ TRACE_EVENT(xrep_nlinks_set_record,
__entry->children)
);
DECLARE_EVENT_CLASS(xrep_dentry_class,
TP_PROTO(struct xfs_mount *mp, const struct dentry *dentry),
TP_ARGS(mp, dentry),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned int, flags)
__field(unsigned long, ino)
__field(bool, positive)
__field(unsigned long, parent_ino)
__field(unsigned int, namelen)
__dynamic_array(char, name, dentry->d_name.len)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->flags = dentry->d_flags;
__entry->positive = d_is_positive(dentry);
if (dentry->d_parent && d_inode(dentry->d_parent))
__entry->parent_ino = d_inode(dentry->d_parent)->i_ino;
else
__entry->parent_ino = -1UL;
__entry->ino = d_inode(dentry) ? d_inode(dentry)->i_ino : 0;
__entry->namelen = dentry->d_name.len;
memcpy(__get_str(name), dentry->d_name.name, dentry->d_name.len);
),
TP_printk("dev %d:%d flags 0x%x positive? %d parent_ino 0x%lx ino 0x%lx name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->flags,
__entry->positive,
__entry->parent_ino,
__entry->ino,
__entry->namelen,
__get_str(name))
);
#define DEFINE_REPAIR_DENTRY_EVENT(name) \
DEFINE_EVENT(xrep_dentry_class, name, \
TP_PROTO(struct xfs_mount *mp, const struct dentry *dentry), \
TP_ARGS(mp, dentry))
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_child);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_alias);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_dentry);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_invalidate_child);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment