Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2017 Oracle.  All Rights Reserved.
0004  * Author: Darrick J. Wong <darrick.wong@oracle.com>
0005  */
0006 #include "xfs.h"
0007 #include "xfs_fs.h"
0008 #include "xfs_shared.h"
0009 #include "xfs_format.h"
0010 #include "xfs_trans_resv.h"
0011 #include "xfs_mount.h"
0012 #include "xfs_log_format.h"
0013 #include "xfs_inode.h"
0014 #include "xfs_icache.h"
0015 #include "xfs_dir2.h"
0016 #include "xfs_dir2_priv.h"
0017 #include "scrub/scrub.h"
0018 #include "scrub/common.h"
0019 
0020 /* Set us up to scrub parents. */
0021 int
0022 xchk_setup_parent(
0023     struct xfs_scrub    *sc)
0024 {
0025     return xchk_setup_inode_contents(sc, 0);
0026 }
0027 
0028 /* Parent pointers */
0029 
0030 /* Look for an entry in a parent pointing to this inode. */
0031 
0032 struct xchk_parent_ctx {
0033     struct dir_context  dc;
0034     struct xfs_scrub    *sc;
0035     xfs_ino_t       ino;
0036     xfs_nlink_t     nlink;
0037     bool            cancelled;
0038 };
0039 
0040 /* Look for a single entry in a directory pointing to an inode. */
0041 STATIC int
0042 xchk_parent_actor(
0043     struct dir_context  *dc,
0044     const char      *name,
0045     int         namelen,
0046     loff_t          pos,
0047     u64         ino,
0048     unsigned        type)
0049 {
0050     struct xchk_parent_ctx  *spc;
0051     int         error = 0;
0052 
0053     spc = container_of(dc, struct xchk_parent_ctx, dc);
0054     if (spc->ino == ino)
0055         spc->nlink++;
0056 
0057     /*
0058      * If we're facing a fatal signal, bail out.  Store the cancellation
0059      * status separately because the VFS readdir code squashes error codes
0060      * into short directory reads.
0061      */
0062     if (xchk_should_terminate(spc->sc, &error))
0063         spc->cancelled = true;
0064 
0065     return error;
0066 }
0067 
0068 /* Count the number of dentries in the parent dir that point to this inode. */
0069 STATIC int
0070 xchk_parent_count_parent_dentries(
0071     struct xfs_scrub    *sc,
0072     struct xfs_inode    *parent,
0073     xfs_nlink_t     *nlink)
0074 {
0075     struct xchk_parent_ctx  spc = {
0076         .dc.actor   = xchk_parent_actor,
0077         .ino        = sc->ip->i_ino,
0078         .sc     = sc,
0079     };
0080     size_t          bufsize;
0081     loff_t          oldpos;
0082     uint            lock_mode;
0083     int         error = 0;
0084 
0085     /*
0086      * If there are any blocks, read-ahead block 0 as we're almost
0087      * certain to have the next operation be a read there.  This is
0088      * how we guarantee that the parent's extent map has been loaded,
0089      * if there is one.
0090      */
0091     lock_mode = xfs_ilock_data_map_shared(parent);
0092     if (parent->i_df.if_nextents > 0)
0093         error = xfs_dir3_data_readahead(parent, 0, 0);
0094     xfs_iunlock(parent, lock_mode);
0095     if (error)
0096         return error;
0097 
0098     /*
0099      * Iterate the parent dir to confirm that there is
0100      * exactly one entry pointing back to the inode being
0101      * scanned.
0102      */
0103     bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE,
0104             parent->i_disk_size);
0105     oldpos = 0;
0106     while (true) {
0107         error = xfs_readdir(sc->tp, parent, &spc.dc, bufsize);
0108         if (error)
0109             goto out;
0110         if (spc.cancelled) {
0111             error = -EAGAIN;
0112             goto out;
0113         }
0114         if (oldpos == spc.dc.pos)
0115             break;
0116         oldpos = spc.dc.pos;
0117     }
0118     *nlink = spc.nlink;
0119 out:
0120     return error;
0121 }
0122 
0123 /*
0124  * Given the inode number of the alleged parent of the inode being
0125  * scrubbed, try to validate that the parent has exactly one directory
0126  * entry pointing back to the inode being scrubbed.
0127  */
0128 STATIC int
0129 xchk_parent_validate(
0130     struct xfs_scrub    *sc,
0131     xfs_ino_t       dnum,
0132     bool            *try_again)
0133 {
0134     struct xfs_mount    *mp = sc->mp;
0135     struct xfs_inode    *dp = NULL;
0136     xfs_nlink_t     expected_nlink;
0137     xfs_nlink_t     nlink;
0138     int         error = 0;
0139 
0140     *try_again = false;
0141 
0142     if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
0143         goto out;
0144 
0145     /* '..' must not point to ourselves. */
0146     if (sc->ip->i_ino == dnum) {
0147         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0148         goto out;
0149     }
0150 
0151     /*
0152      * If we're an unlinked directory, the parent /won't/ have a link
0153      * to us.  Otherwise, it should have one link.
0154      */
0155     expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
0156 
0157     /*
0158      * Grab this parent inode.  We release the inode before we
0159      * cancel the scrub transaction.  Since we're don't know a
0160      * priori that releasing the inode won't trigger eofblocks
0161      * cleanup (which allocates what would be a nested transaction)
0162      * if the parent pointer erroneously points to a file, we
0163      * can't use DONTCACHE here because DONTCACHE inodes can trigger
0164      * immediate inactive cleanup of the inode.
0165      *
0166      * If _iget returns -EINVAL or -ENOENT then the parent inode number is
0167      * garbage and the directory is corrupt.  If the _iget returns
0168      * -EFSCORRUPTED or -EFSBADCRC then the parent is corrupt which is a
0169      *  cross referencing error.  Any other error is an operational error.
0170      */
0171     error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
0172     if (error == -EINVAL || error == -ENOENT) {
0173         error = -EFSCORRUPTED;
0174         xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
0175         goto out;
0176     }
0177     if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
0178         goto out;
0179     if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
0180         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0181         goto out_rele;
0182     }
0183 
0184     /*
0185      * We prefer to keep the inode locked while we lock and search
0186      * its alleged parent for a forward reference.  If we can grab
0187      * the iolock, validate the pointers and we're done.  We must
0188      * use nowait here to avoid an ABBA deadlock on the parent and
0189      * the child inodes.
0190      */
0191     if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
0192         error = xchk_parent_count_parent_dentries(sc, dp, &nlink);
0193         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
0194                 &error))
0195             goto out_unlock;
0196         if (nlink != expected_nlink)
0197             xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0198         goto out_unlock;
0199     }
0200 
0201     /*
0202      * The game changes if we get here.  We failed to lock the parent,
0203      * so we're going to try to verify both pointers while only holding
0204      * one lock so as to avoid deadlocking with something that's actually
0205      * trying to traverse down the directory tree.
0206      */
0207     xfs_iunlock(sc->ip, sc->ilock_flags);
0208     sc->ilock_flags = 0;
0209     error = xchk_ilock_inverted(dp, XFS_IOLOCK_SHARED);
0210     if (error)
0211         goto out_rele;
0212 
0213     /* Go looking for our dentry. */
0214     error = xchk_parent_count_parent_dentries(sc, dp, &nlink);
0215     if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
0216         goto out_unlock;
0217 
0218     /* Drop the parent lock, relock this inode. */
0219     xfs_iunlock(dp, XFS_IOLOCK_SHARED);
0220     error = xchk_ilock_inverted(sc->ip, XFS_IOLOCK_EXCL);
0221     if (error)
0222         goto out_rele;
0223     sc->ilock_flags = XFS_IOLOCK_EXCL;
0224 
0225     /*
0226      * If we're an unlinked directory, the parent /won't/ have a link
0227      * to us.  Otherwise, it should have one link.  We have to re-set
0228      * it here because we dropped the lock on sc->ip.
0229      */
0230     expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
0231 
0232     /* Look up '..' to see if the inode changed. */
0233     error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL);
0234     if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
0235         goto out_rele;
0236 
0237     /* Drat, parent changed.  Try again! */
0238     if (dnum != dp->i_ino) {
0239         xfs_irele(dp);
0240         *try_again = true;
0241         return 0;
0242     }
0243     xfs_irele(dp);
0244 
0245     /*
0246      * '..' didn't change, so check that there was only one entry
0247      * for us in the parent.
0248      */
0249     if (nlink != expected_nlink)
0250         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0251     return error;
0252 
0253 out_unlock:
0254     xfs_iunlock(dp, XFS_IOLOCK_SHARED);
0255 out_rele:
0256     xfs_irele(dp);
0257 out:
0258     return error;
0259 }
0260 
0261 /* Scrub a parent pointer. */
0262 int
0263 xchk_parent(
0264     struct xfs_scrub    *sc)
0265 {
0266     struct xfs_mount    *mp = sc->mp;
0267     xfs_ino_t       dnum;
0268     bool            try_again;
0269     int         tries = 0;
0270     int         error = 0;
0271 
0272     /*
0273      * If we're a directory, check that the '..' link points up to
0274      * a directory that has one entry pointing to us.
0275      */
0276     if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
0277         return -ENOENT;
0278 
0279     /* We're not a special inode, are we? */
0280     if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {
0281         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0282         goto out;
0283     }
0284 
0285     /*
0286      * The VFS grabs a read or write lock via i_rwsem before it reads
0287      * or writes to a directory.  If we've gotten this far we've
0288      * already obtained IOLOCK_EXCL, which (since 4.10) is the same as
0289      * getting a write lock on i_rwsem.  Therefore, it is safe for us
0290      * to drop the ILOCK here in order to do directory lookups.
0291      */
0292     sc->ilock_flags &= ~(XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL);
0293     xfs_iunlock(sc->ip, XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL);
0294 
0295     /* Look up '..' */
0296     error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL);
0297     if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
0298         goto out;
0299     if (!xfs_verify_dir_ino(mp, dnum)) {
0300         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0301         goto out;
0302     }
0303 
0304     /* Is this the root dir?  Then '..' must point to itself. */
0305     if (sc->ip == mp->m_rootip) {
0306         if (sc->ip->i_ino != mp->m_sb.sb_rootino ||
0307             sc->ip->i_ino != dnum)
0308             xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
0309         goto out;
0310     }
0311 
0312     do {
0313         error = xchk_parent_validate(sc, dnum, &try_again);
0314         if (error)
0315             goto out;
0316     } while (try_again && ++tries < 20);
0317 
0318     /*
0319      * We gave it our best shot but failed, so mark this scrub
0320      * incomplete.  Userspace can decide if it wants to try again.
0321      */
0322     if (try_again && tries == 20)
0323         xchk_set_incomplete(sc);
0324 out:
0325     /*
0326      * If we failed to lock the parent inode even after a retry, just mark
0327      * this scrub incomplete and return.
0328      */
0329     if ((sc->flags & XCHK_TRY_HARDER) && error == -EDEADLOCK) {
0330         error = 0;
0331         xchk_set_incomplete(sc);
0332     }
0333     return error;
0334 }