diff options
Diffstat (limited to 'fs/xfs/scrub/parent_repair.c')
-rw-r--r-- | fs/xfs/scrub/parent_repair.c | 97 |
1 files changed, 96 insertions, 1 deletions
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c index 648345b1c1a9..134726bb20bf 100644 --- a/fs/xfs/scrub/parent_repair.c +++ b/fs/xfs/scrub/parent_repair.c @@ -30,6 +30,7 @@ #include "scrub/repair.h" #include "scrub/iscan.h" #include "scrub/parent.h" +#include "scrub/orphanage.h" struct xrep_findparent_info { /* The directory currently being scanning, and a readdir context. */ @@ -371,6 +372,34 @@ xrep_parent_scan( return 0; } +static inline struct xrep_orphanage_req * +xrep_parent_orphanage_req( + struct xfs_scrub *sc) +{ + return sc->buf; +} + +static inline unsigned char * +xrep_parent_orphanage_namebuf( + struct xfs_scrub *sc) +{ + return (unsigned char *)(((struct xrep_orphanage_req *)sc->buf) + 1); +} + +/* Set up for a parent repair. */ +int +xrep_setup_parent( + struct xfs_scrub *sc) +{ + /* We need a buffer for the orphanage request and a name buffer. */ + sc->buf = kvmalloc(xrep_orphanage_req_sizeof(), + GFP_KERNEL | __GFP_NOWARN | __GFP_RETRY_MAYFAIL); + if (!sc->buf) + return -ENOMEM; + + return xrep_orphanage_try_create(sc); +} + /* * Repairing The Directory Parent Pointer * ====================================== @@ -405,6 +434,72 @@ xrep_parent_reset_dir( spaceres); } +/* + * Move the current file to the orphanage. Caller must not hold any inode + * locks. Upon return, the scrub state will reflect the transaction, ijoin, + * and inode lock states. + */ +STATIC int +xrep_parent_move_to_orphanage( + struct xfs_scrub *sc) +{ + struct xrep_orphanage_req *orph = xrep_parent_orphanage_req(sc); + unsigned char *namebuf = xrep_parent_orphanage_namebuf(sc); + int error; + + /* No orphanage? We can't fix this. */ + if (!sc->orphanage) + return -EFSCORRUPTED; + + /* If we can take the orphanage's iolock then we're ready to move. */ + if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) { + xfs_ino_t orig_parent, new_parent; + + /* + * We may have to drop the lock on sc->ip to try to lock the + * orphanage. Therefore, look up the old dotdot entry for + * sc->ip so that we can compare it after we re-lock sc->ip. + */ + orig_parent = xrep_dotdot_lookup(sc); + + xchk_iunlock(sc, sc->ilock_flags); + error = xrep_orphanage_iolock_two(sc); + if (error) + return error; + + /* + * 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. + */ + new_parent = xrep_dotdot_lookup(sc); + + if (orig_parent != new_parent || VFS_I(sc->ip)->i_nlink == 0) + return 0; + } + + /* + * Move the directory to the orphanage, and let scrub teardown unlock + * everything for us. + */ + xrep_orphanage_compute_blkres(sc, orph); + + error = xrep_orphanage_compute_name(orph, namebuf); + if (error) + return error; + + error = xfs_trans_reserve_more(sc->tp, + orph->orphanage_blkres + orph->child_blkres, 0); + if (error) + return error; + + error = xrep_orphanage_ilock_resv_quota(orph); + if (error) + return error; + + return xrep_orphanage_adopt(orph); +} + int xrep_parent( struct xfs_scrub *sc) @@ -433,7 +528,7 @@ xrep_parent( if (error) return error; if (parent_ino == NULLFSINO) - return -EFSCORRUPTED; + return xrep_parent_move_to_orphanage(sc); reset_parent: /* If the '..' entry is already set to the parent inode, we're done. */ |