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

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
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -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;
}

+42 −0
Original line number Diff line number Diff line
@@ -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 */