Commit 1e58a8cc authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: move orphan files to the orphanage

When we're repairing a directory structure or fixing the dotdot entry of
a subdirectory, it's possible that we won't ever find a parent for the
subdirectory.  When this is the case, move it to the orphanage, aka
/lost+found.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 34c9382c
......@@ -4778,14 +4778,21 @@ Orphaned files are adopted by the orphanage as follows:
The ``xrep_orphanage_iolock_two`` function follows the inode locking
strategy discussed earlier.
3. Call ``xrep_orphanage_compute_blkres`` and ``xrep_orphanage_compute_name``
to compute the new name in the orphanage and the block reservation required.
4. Use ``xrep_orphanage_adoption_prep`` to reserve resources to the repair
3. Use ``xrep_adoption_trans_alloc`` to reserve resources to the repair
transaction.
5. Call ``xrep_orphanage_adopt`` to reparent the orphaned file into the lost
and found, and update the kernel dentry cache.
4. Call ``xrep_orphanage_compute_name`` to compute the new name in the
orphanage.
5. If the adoption is going to happen, call ``xrep_adoption_reparent`` to
reparent the orphaned file into the lost and found and invalidate the dentry
cache.
6. Call ``xrep_adoption_finish`` to commit any filesystem updates, release the
orphanage ILOCK, and clean the scrub transaction.
7. If a runtime error happens, call ``xrep_adoption_cancel`` to release all
resources.
The proposed patches are in the
`orphanage adoption
......
......@@ -205,6 +205,7 @@ xfs-y += $(addprefix scrub/, \
inode_repair.o \
newbt.o \
nlinks_repair.o \
orphanage.o \
parent_repair.o \
rcbag_btree.o \
rcbag.o \
......
......@@ -42,6 +42,7 @@
#include "scrub/readdir.h"
#include "scrub/reap.h"
#include "scrub/findparent.h"
#include "scrub/orphanage.h"
/*
* Directory Repair
......@@ -115,12 +116,21 @@ struct xrep_dir {
*/
struct xrep_parent_scan_info pscan;
/*
* Context information for attaching this directory to the lost+found
* if this directory does not have a parent.
*/
struct xrep_adoption adoption;
/* How many subdirectories did we find? */
uint64_t subdirs;
/* How many dirents did we find? */
unsigned int dirents;
/* Should we move this directory to the orphanage? */
bool needs_adoption;
/* Directory entry name, plus the trailing null. */
struct xfs_name xname;
unsigned char namebuf[MAXNAMELEN];
......@@ -148,6 +158,10 @@ xrep_setup_directory(
xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
error = xrep_orphanage_try_create(sc);
if (error)
return error;
error = xrep_tempfile_create(sc, S_IFDIR);
if (error)
return error;
......@@ -1137,10 +1151,8 @@ xrep_dir_set_nlink(
/*
* The directory is not on the incore unlinked list, which means that
* it needs to be reachable via the directory tree. Update the nlink
* with our observed link count.
*
* XXX: A subsequent patch will handle parentless directories by moving
* them to the lost and found instead of aborting the repair.
* with our observed link count. If the directory has no parent, it
* will be moved to the orphanage.
*/
if (!xfs_inode_on_unlinked_list(dp))
goto reset_nlink;
......@@ -1151,6 +1163,7 @@ xrep_dir_set_nlink(
* inactivate when the last reference drops.
*/
if (rd->dirents == 0) {
rd->needs_adoption = false;
new_nlink = 0;
goto reset_nlink;
}
......@@ -1159,7 +1172,8 @@ xrep_dir_set_nlink(
* The directory is on the unlinked list and we found dirents. This
* directory needs to be reachable via the directory tree. Remove the
* dir from the unlinked list and update nlink with the observed link
* count.
* count. If the directory has no parent, it will be moved to the
* orphanage.
*/
pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, dp->i_ino));
if (!pag) {
......@@ -1195,12 +1209,16 @@ xrep_dir_swap(
return -EFSCORRUPTED;
/*
* If we never found the parent for this directory, we can't fix this
* directory.
* If we never found the parent for this directory, temporarily assign
* the root dir as the parent; we'll move this to the orphanage after
* exchanging the dir contents. We hold the ILOCK of the dir being
* repaired, so we're not worried about racy updates of dotdot.
*/
ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
if (rd->pscan.parent_ino == NULLFSINO)
return -EFSCORRUPTED;
if (rd->pscan.parent_ino == NULLFSINO) {
rd->needs_adoption = true;
rd->pscan.parent_ino = rd->sc->mp->m_sb.sb_rootino;
}
/*
* Reset the temporary directory's '..' entry to point to the parent
......@@ -1358,6 +1376,91 @@ xrep_dir_setup_scan(
return error;
}
/*
* Move the current file to the orphanage.
*
* Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks. Upon
* successful return, the scrub transaction will have enough extra reservation
* to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the
* orphanage; and both inodes will be ijoined.
*/
STATIC int
xrep_dir_move_to_orphanage(
struct xrep_dir *rd)
{
struct xfs_scrub *sc = rd->sc;
xfs_ino_t orig_parent, new_parent;
int error;
/*
* We are about to drop the ILOCK on sc->ip to lock the orphanage and
* prepare for the adoption. Therefore, look up the old dotdot entry
* for sc->ip so that we can compare it after we re-lock sc->ip.
*/
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &orig_parent);
if (error)
return error;
/*
* Drop the ILOCK on the scrub target and commit the transaction.
* Adoption computes its own resource requirements and gathers the
* necessary components.
*/
error = xrep_trans_commit(sc);
if (error)
return error;
xchk_iunlock(sc, XFS_ILOCK_EXCL);
/* If we can take the orphanage's iolock then we're ready to move. */
if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
xchk_iunlock(sc, sc->ilock_flags);
error = xrep_orphanage_iolock_two(sc);
if (error)
return error;
}
/* Grab transaction and ILOCK the two files. */
error = xrep_adoption_trans_alloc(sc, &rd->adoption);
if (error)
return error;
error = xrep_adoption_compute_name(&rd->adoption, &rd->xname);
if (error)
return error;
/*
* Now that we've reacquired the ILOCK on sc->ip, look up the dotdot
* entry again. If the parent changed or the child was unlinked while
* the child directory was unlocked, we don't need to move the child to
* the orphanage after all.
*/
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &new_parent);
if (error)
return error;
/*
* Attach to the orphanage if we still have a linked directory and it
* hasn't been moved.
*/
if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) {
error = xrep_adoption_move(&rd->adoption);
if (error)
return error;
}
/*
* Launder the scrub transaction so we can drop the orphanage ILOCK
* and IOLOCK. Return holding the scrub target's ILOCK and IOLOCK.
*/
error = xrep_adoption_trans_roll(&rd->adoption);
if (error)
return error;
xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
return 0;
}
/*
* Repair the directory metadata.
*
......@@ -1396,6 +1499,15 @@ xrep_directory(
if (error)
goto out_teardown;
if (rd->needs_adoption) {
if (!xrep_orphanage_can_adopt(rd->sc))
error = -EFSCORRUPTED;
else
error = xrep_dir_move_to_orphanage(rd);
if (error)
goto out_teardown;
}
out_teardown:
xrep_dir_teardown(sc);
return error;
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_ORPHANAGE_H__
#define __XFS_SCRUB_ORPHANAGE_H__
#ifdef CONFIG_XFS_ONLINE_REPAIR
int xrep_orphanage_create(struct xfs_scrub *sc);
/*
* If we're doing a repair, ensure that the orphanage exists and attach it to
* the scrub context.
*/
static inline int
xrep_orphanage_try_create(
struct xfs_scrub *sc)
{
int error;
ASSERT(sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR);
error = xrep_orphanage_create(sc);
switch (error) {
case 0:
case -ENOENT:
case -ENOTDIR:
case -ENOSPC:
/*
* If the orphanage can't be found or isn't a directory, we'll
* keep going, but we won't be able to attach the file to the
* orphanage if we can't find the parent.
*/
return 0;
}
return error;
}
int xrep_orphanage_iolock_two(struct xfs_scrub *sc);
void xrep_orphanage_ilock(struct xfs_scrub *sc, unsigned int ilock_flags);
bool xrep_orphanage_ilock_nowait(struct xfs_scrub *sc,
unsigned int ilock_flags);
void xrep_orphanage_iunlock(struct xfs_scrub *sc, unsigned int ilock_flags);
void xrep_orphanage_rele(struct xfs_scrub *sc);
/* Information about a request to add a file to the orphanage. */
struct xrep_adoption {
struct xfs_scrub *sc;
/* Name used for the adoption. */
struct xfs_name *xname;
/* Block reservations for orphanage and child (if directory). */
unsigned int orphanage_blkres;
unsigned int child_blkres;
};
bool xrep_orphanage_can_adopt(struct xfs_scrub *sc);
int xrep_adoption_trans_alloc(struct xfs_scrub *sc,
struct xrep_adoption *adopt);
int xrep_adoption_compute_name(struct xrep_adoption *adopt,
struct xfs_name *xname);
int xrep_adoption_move(struct xrep_adoption *adopt);
int xrep_adoption_trans_roll(struct xrep_adoption *adopt);
#else
struct xrep_adoption { /* empty */ };
# define xrep_orphanage_rele(sc) ((void)0)
#endif /* CONFIG_XFS_ONLINE_REPAIR */
#endif /* __XFS_SCRUB_ORPHANAGE_H__ */
......@@ -32,6 +32,8 @@
#include "scrub/iscan.h"
#include "scrub/findparent.h"
#include "scrub/readdir.h"
#include "scrub/tempfile.h"
#include "scrub/orphanage.h"
/*
* Repairing The Directory Parent Pointer
......@@ -57,6 +59,13 @@ struct xrep_parent {
* dotdot entry for this directory.
*/
struct xrep_parent_scan_info pscan;
/* Orphanage reparenting request. */
struct xrep_adoption adoption;
/* Directory entry name, plus the trailing null. */
struct xfs_name xname;
unsigned char namebuf[MAXNAMELEN];
};
/* Tear down all the incore stuff we created. */
......@@ -80,9 +89,10 @@ xrep_setup_parent(
if (!rp)
return -ENOMEM;
rp->sc = sc;
rp->xname.name = rp->namebuf;
sc->buf = rp;
return 0;
return xrep_orphanage_try_create(sc);
}
/*
......@@ -179,6 +189,91 @@ xrep_parent_reset_dotdot(
return xfs_trans_roll(&sc->tp);
}
/*
* Move the current file to the orphanage.
*
* Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks. Upon
* successful return, the scrub transaction will have enough extra reservation
* to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the
* orphanage; and both inodes will be ijoined.
*/
STATIC int
xrep_parent_move_to_orphanage(
struct xrep_parent *rp)
{
struct xfs_scrub *sc = rp->sc;
xfs_ino_t orig_parent, new_parent;
int error;
/*
* We are about to drop the ILOCK on sc->ip to lock the orphanage and
* prepare for the adoption. Therefore, look up the old dotdot entry
* for sc->ip so that we can compare it after we re-lock sc->ip.
*/
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &orig_parent);
if (error)
return error;
/*
* Drop the ILOCK on the scrub target and commit the transaction.
* Adoption computes its own resource requirements and gathers the
* necessary components.
*/
error = xrep_trans_commit(sc);
if (error)
return error;
xchk_iunlock(sc, XFS_ILOCK_EXCL);
/* If we can take the orphanage's iolock then we're ready to move. */
if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
xchk_iunlock(sc, sc->ilock_flags);
error = xrep_orphanage_iolock_two(sc);
if (error)
return error;
}
/* Grab transaction and ILOCK the two files. */
error = xrep_adoption_trans_alloc(sc, &rp->adoption);
if (error)
return error;
error = xrep_adoption_compute_name(&rp->adoption, &rp->xname);
if (error)
return error;
/*
* Now that we've reacquired the ILOCK on sc->ip, look up the dotdot
* entry again. If the parent changed or the child was unlinked while
* the child directory was unlocked, we don't need to move the child to
* the orphanage after all.
*/
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &new_parent);
if (error)
return error;
/*
* Attach to the orphanage if we still have a linked directory and it
* hasn't been moved.
*/
if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) {
error = xrep_adoption_move(&rp->adoption);
if (error)
return error;
}
/*
* Launder the scrub transaction so we can drop the orphanage ILOCK
* and IOLOCK. Return holding the scrub target's ILOCK and IOLOCK.
*/
error = xrep_adoption_trans_roll(&rp->adoption);
if (error)
return error;
xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
return 0;
}
/*
* Commit the new parent pointer structure (currently only the dotdot entry) to
* the file that we're repairing.
......@@ -188,7 +283,8 @@ xrep_parent_rebuild_tree(
struct xrep_parent *rp)
{
if (rp->pscan.parent_ino == NULLFSINO) {
/* Cannot fix orphaned directories yet. */
if (xrep_orphanage_can_adopt(rp->sc))
return xrep_parent_move_to_orphanage(rp);
return -EFSCORRUPTED;
}
......
......@@ -27,6 +27,7 @@
#include "scrub/stats.h"
#include "scrub/xfile.h"
#include "scrub/tempfile.h"
#include "scrub/orphanage.h"
/*
* Online Scrub and Repair
......@@ -217,6 +218,7 @@ xchk_teardown(
}
xrep_tempfile_rele(sc);
xrep_orphanage_rele(sc);
xchk_fsgates_disable(sc);
return error;
}
......
......@@ -105,6 +105,10 @@ struct xfs_scrub {
/* Lock flags for @ip. */
uint ilock_flags;
/* The orphanage, for stashing files that have lost their parent. */
uint orphanage_ilock_flags;
struct xfs_inode *orphanage;
/* A temporary file on this filesystem, for staging new metadata. */
struct xfs_inode *tempip;
uint temp_ilock_flags;
......
......@@ -2588,6 +2588,34 @@ DEFINE_EVENT(xrep_dirent_class, name, \
DEFINE_XREP_DIRENT_EVENT(xrep_dir_salvage_entry);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_stash_createname);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_replay_createname);
DEFINE_XREP_DIRENT_EVENT(xrep_adoption_reparent);
DECLARE_EVENT_CLASS(xrep_adoption_class,
TP_PROTO(struct xfs_inode *dp, struct xfs_inode *ip, bool moved),
TP_ARGS(dp, ip, moved),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, dir_ino)
__field(xfs_ino_t, child_ino)
__field(bool, moved)
),
TP_fast_assign(
__entry->dev = dp->i_mount->m_super->s_dev;
__entry->dir_ino = dp->i_ino;
__entry->child_ino = ip->i_ino;
__entry->moved = moved;
),
TP_printk("dev %d:%d dir 0x%llx child 0x%llx moved? %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->dir_ino,
__entry->child_ino,
__entry->moved)
);
#define DEFINE_XREP_ADOPTION_EVENT(name) \
DEFINE_EVENT(xrep_adoption_class, name, \
TP_PROTO(struct xfs_inode *dp, struct xfs_inode *ip, bool moved), \
TP_ARGS(dp, ip, moved))
DEFINE_XREP_ADOPTION_EVENT(xrep_adoption_trans_roll);
DECLARE_EVENT_CLASS(xrep_parent_salvage_class,
TP_PROTO(struct xfs_inode *dp, xfs_ino_t ino),
......
......@@ -914,10 +914,10 @@ xfs_droplink(
/*
* Increment the link count on an inode & log the change.
*/
static void
void
xfs_bumplink(
xfs_trans_t *tp,
xfs_inode_t *ip)
struct xfs_trans *tp,
struct xfs_inode *ip)
{
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
......
......@@ -625,6 +625,7 @@ void xfs_end_io(struct work_struct *work);
int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_iunlock2_remapping(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_bumplink(struct xfs_trans *tp, struct xfs_inode *ip);
static inline bool
xfs_inode_unlinked_incomplete(
......
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