root/fs/xfs/scrub/parent.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xchk_setup_parent
  2. xchk_parent_actor
  3. xchk_parent_count_parent_dentries
  4. xchk_parent_validate
  5. xchk_parent

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright (C) 2017 Oracle.  All Rights Reserved.
   4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
   5  */
   6 #include "xfs.h"
   7 #include "xfs_fs.h"
   8 #include "xfs_shared.h"
   9 #include "xfs_format.h"
  10 #include "xfs_trans_resv.h"
  11 #include "xfs_mount.h"
  12 #include "xfs_log_format.h"
  13 #include "xfs_inode.h"
  14 #include "xfs_icache.h"
  15 #include "xfs_dir2.h"
  16 #include "xfs_dir2_priv.h"
  17 #include "scrub/scrub.h"
  18 #include "scrub/common.h"
  19 
  20 /* Set us up to scrub parents. */
  21 int
  22 xchk_setup_parent(
  23         struct xfs_scrub        *sc,
  24         struct xfs_inode        *ip)
  25 {
  26         return xchk_setup_inode_contents(sc, ip, 0);
  27 }
  28 
  29 /* Parent pointers */
  30 
  31 /* Look for an entry in a parent pointing to this inode. */
  32 
  33 struct xchk_parent_ctx {
  34         struct dir_context      dc;
  35         xfs_ino_t               ino;
  36         xfs_nlink_t             nlink;
  37 };
  38 
  39 /* Look for a single entry in a directory pointing to an inode. */
  40 STATIC int
  41 xchk_parent_actor(
  42         struct dir_context      *dc,
  43         const char              *name,
  44         int                     namelen,
  45         loff_t                  pos,
  46         u64                     ino,
  47         unsigned                type)
  48 {
  49         struct xchk_parent_ctx  *spc;
  50 
  51         spc = container_of(dc, struct xchk_parent_ctx, dc);
  52         if (spc->ino == ino)
  53                 spc->nlink++;
  54         return 0;
  55 }
  56 
  57 /* Count the number of dentries in the parent dir that point to this inode. */
  58 STATIC int
  59 xchk_parent_count_parent_dentries(
  60         struct xfs_scrub        *sc,
  61         struct xfs_inode        *parent,
  62         xfs_nlink_t             *nlink)
  63 {
  64         struct xchk_parent_ctx  spc = {
  65                 .dc.actor = xchk_parent_actor,
  66                 .dc.pos = 0,
  67                 .ino = sc->ip->i_ino,
  68                 .nlink = 0,
  69         };
  70         size_t                  bufsize;
  71         loff_t                  oldpos;
  72         uint                    lock_mode;
  73         int                     error = 0;
  74 
  75         /*
  76          * If there are any blocks, read-ahead block 0 as we're almost
  77          * certain to have the next operation be a read there.  This is
  78          * how we guarantee that the parent's extent map has been loaded,
  79          * if there is one.
  80          */
  81         lock_mode = xfs_ilock_data_map_shared(parent);
  82         if (parent->i_d.di_nextents > 0)
  83                 error = xfs_dir3_data_readahead(parent, 0, -1);
  84         xfs_iunlock(parent, lock_mode);
  85         if (error)
  86                 return error;
  87 
  88         /*
  89          * Iterate the parent dir to confirm that there is
  90          * exactly one entry pointing back to the inode being
  91          * scanned.
  92          */
  93         bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE,
  94                         parent->i_d.di_size);
  95         oldpos = 0;
  96         while (true) {
  97                 error = xfs_readdir(sc->tp, parent, &spc.dc, bufsize);
  98                 if (error)
  99                         goto out;
 100                 if (oldpos == spc.dc.pos)
 101                         break;
 102                 oldpos = spc.dc.pos;
 103         }
 104         *nlink = spc.nlink;
 105 out:
 106         return error;
 107 }
 108 
 109 /*
 110  * Given the inode number of the alleged parent of the inode being
 111  * scrubbed, try to validate that the parent has exactly one directory
 112  * entry pointing back to the inode being scrubbed.
 113  */
 114 STATIC int
 115 xchk_parent_validate(
 116         struct xfs_scrub        *sc,
 117         xfs_ino_t               dnum,
 118         bool                    *try_again)
 119 {
 120         struct xfs_mount        *mp = sc->mp;
 121         struct xfs_inode        *dp = NULL;
 122         xfs_nlink_t             expected_nlink;
 123         xfs_nlink_t             nlink;
 124         int                     error = 0;
 125 
 126         *try_again = false;
 127 
 128         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 129                 goto out;
 130 
 131         /* '..' must not point to ourselves. */
 132         if (sc->ip->i_ino == dnum) {
 133                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 134                 goto out;
 135         }
 136 
 137         /*
 138          * If we're an unlinked directory, the parent /won't/ have a link
 139          * to us.  Otherwise, it should have one link.
 140          */
 141         expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
 142 
 143         /*
 144          * Grab this parent inode.  We release the inode before we
 145          * cancel the scrub transaction.  Since we're don't know a
 146          * priori that releasing the inode won't trigger eofblocks
 147          * cleanup (which allocates what would be a nested transaction)
 148          * if the parent pointer erroneously points to a file, we
 149          * can't use DONTCACHE here because DONTCACHE inodes can trigger
 150          * immediate inactive cleanup of the inode.
 151          *
 152          * If _iget returns -EINVAL then the parent inode number is garbage
 153          * and the directory is corrupt.  If the _iget returns -EFSCORRUPTED
 154          * or -EFSBADCRC then the parent is corrupt which is a cross
 155          * referencing error.  Any other error is an operational error.
 156          */
 157         error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
 158         if (error == -EINVAL) {
 159                 error = -EFSCORRUPTED;
 160                 xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
 161                 goto out;
 162         }
 163         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 164                 goto out;
 165         if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
 166                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 167                 goto out_rele;
 168         }
 169 
 170         /*
 171          * We prefer to keep the inode locked while we lock and search
 172          * its alleged parent for a forward reference.  If we can grab
 173          * the iolock, validate the pointers and we're done.  We must
 174          * use nowait here to avoid an ABBA deadlock on the parent and
 175          * the child inodes.
 176          */
 177         if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
 178                 error = xchk_parent_count_parent_dentries(sc, dp, &nlink);
 179                 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
 180                                 &error))
 181                         goto out_unlock;
 182                 if (nlink != expected_nlink)
 183                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 184                 goto out_unlock;
 185         }
 186 
 187         /*
 188          * The game changes if we get here.  We failed to lock the parent,
 189          * so we're going to try to verify both pointers while only holding
 190          * one lock so as to avoid deadlocking with something that's actually
 191          * trying to traverse down the directory tree.
 192          */
 193         xfs_iunlock(sc->ip, sc->ilock_flags);
 194         sc->ilock_flags = 0;
 195         error = xchk_ilock_inverted(dp, XFS_IOLOCK_SHARED);
 196         if (error)
 197                 goto out_rele;
 198 
 199         /* Go looking for our dentry. */
 200         error = xchk_parent_count_parent_dentries(sc, dp, &nlink);
 201         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 202                 goto out_unlock;
 203 
 204         /* Drop the parent lock, relock this inode. */
 205         xfs_iunlock(dp, XFS_IOLOCK_SHARED);
 206         error = xchk_ilock_inverted(sc->ip, XFS_IOLOCK_EXCL);
 207         if (error)
 208                 goto out_rele;
 209         sc->ilock_flags = XFS_IOLOCK_EXCL;
 210 
 211         /*
 212          * If we're an unlinked directory, the parent /won't/ have a link
 213          * to us.  Otherwise, it should have one link.  We have to re-set
 214          * it here because we dropped the lock on sc->ip.
 215          */
 216         expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
 217 
 218         /* Look up '..' to see if the inode changed. */
 219         error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL);
 220         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
 221                 goto out_rele;
 222 
 223         /* Drat, parent changed.  Try again! */
 224         if (dnum != dp->i_ino) {
 225                 xfs_irele(dp);
 226                 *try_again = true;
 227                 return 0;
 228         }
 229         xfs_irele(dp);
 230 
 231         /*
 232          * '..' didn't change, so check that there was only one entry
 233          * for us in the parent.
 234          */
 235         if (nlink != expected_nlink)
 236                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 237         return error;
 238 
 239 out_unlock:
 240         xfs_iunlock(dp, XFS_IOLOCK_SHARED);
 241 out_rele:
 242         xfs_irele(dp);
 243 out:
 244         return error;
 245 }
 246 
 247 /* Scrub a parent pointer. */
 248 int
 249 xchk_parent(
 250         struct xfs_scrub        *sc)
 251 {
 252         struct xfs_mount        *mp = sc->mp;
 253         xfs_ino_t               dnum;
 254         bool                    try_again;
 255         int                     tries = 0;
 256         int                     error = 0;
 257 
 258         /*
 259          * If we're a directory, check that the '..' link points up to
 260          * a directory that has one entry pointing to us.
 261          */
 262         if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
 263                 return -ENOENT;
 264 
 265         /* We're not a special inode, are we? */
 266         if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {
 267                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 268                 goto out;
 269         }
 270 
 271         /*
 272          * The VFS grabs a read or write lock via i_rwsem before it reads
 273          * or writes to a directory.  If we've gotten this far we've
 274          * already obtained IOLOCK_EXCL, which (since 4.10) is the same as
 275          * getting a write lock on i_rwsem.  Therefore, it is safe for us
 276          * to drop the ILOCK here in order to do directory lookups.
 277          */
 278         sc->ilock_flags &= ~(XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL);
 279         xfs_iunlock(sc->ip, XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL);
 280 
 281         /* Look up '..' */
 282         error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL);
 283         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
 284                 goto out;
 285         if (!xfs_verify_dir_ino(mp, dnum)) {
 286                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 287                 goto out;
 288         }
 289 
 290         /* Is this the root dir?  Then '..' must point to itself. */
 291         if (sc->ip == mp->m_rootip) {
 292                 if (sc->ip->i_ino != mp->m_sb.sb_rootino ||
 293                     sc->ip->i_ino != dnum)
 294                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
 295                 goto out;
 296         }
 297 
 298         do {
 299                 error = xchk_parent_validate(sc, dnum, &try_again);
 300                 if (error)
 301                         goto out;
 302         } while (try_again && ++tries < 20);
 303 
 304         /*
 305          * We gave it our best shot but failed, so mark this scrub
 306          * incomplete.  Userspace can decide if it wants to try again.
 307          */
 308         if (try_again && tries == 20)
 309                 xchk_set_incomplete(sc);
 310 out:
 311         /*
 312          * If we failed to lock the parent inode even after a retry, just mark
 313          * this scrub incomplete and return.
 314          */
 315         if ((sc->flags & XCHK_TRY_HARDER) && error == -EDEADLOCK) {
 316                 error = 0;
 317                 xchk_set_incomplete(sc);
 318         }
 319         return error;
 320 }

/* [<][>][^][v][top][bottom][index][help] */