1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
280e4e126SDarrick J. Wong /*
3ecc73f8aSDarrick J. Wong * Copyright (C) 2017-2023 Oracle. All Rights Reserved.
4739a2fe0SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org>
580e4e126SDarrick J. Wong */
680e4e126SDarrick J. Wong #include "xfs.h"
780e4e126SDarrick J. Wong #include "xfs_fs.h"
880e4e126SDarrick J. Wong #include "xfs_shared.h"
980e4e126SDarrick J. Wong #include "xfs_format.h"
1080e4e126SDarrick J. Wong #include "xfs_trans_resv.h"
1180e4e126SDarrick J. Wong #include "xfs_mount.h"
1280e4e126SDarrick J. Wong #include "xfs_btree.h"
1380e4e126SDarrick J. Wong #include "xfs_log_format.h"
1438bb1310SDarrick J. Wong #include "xfs_trans.h"
1538bb1310SDarrick J. Wong #include "xfs_ag.h"
1680e4e126SDarrick J. Wong #include "xfs_inode.h"
1780e4e126SDarrick J. Wong #include "xfs_ialloc.h"
1838bb1310SDarrick J. Wong #include "xfs_icache.h"
1980e4e126SDarrick J. Wong #include "xfs_da_format.h"
2080e4e126SDarrick J. Wong #include "xfs_reflink.h"
21d852657cSDarrick J. Wong #include "xfs_rmap.h"
22561f648aSDarrick J. Wong #include "xfs_bmap_util.h"
2380e4e126SDarrick J. Wong #include "scrub/scrub.h"
2480e4e126SDarrick J. Wong #include "scrub/common.h"
252e6f2756SDarrick J. Wong #include "scrub/btree.h"
2638bb1310SDarrick J. Wong #include "scrub/trace.h"
2738bb1310SDarrick J. Wong
2838bb1310SDarrick J. Wong /* Prepare the attached inode for scrubbing. */
2938bb1310SDarrick J. Wong static inline int
xchk_prepare_iscrub(struct xfs_scrub * sc)3038bb1310SDarrick J. Wong xchk_prepare_iscrub(
3138bb1310SDarrick J. Wong struct xfs_scrub *sc)
3238bb1310SDarrick J. Wong {
3338bb1310SDarrick J. Wong int error;
3438bb1310SDarrick J. Wong
35294012fbSDarrick J. Wong xchk_ilock(sc, XFS_IOLOCK_EXCL);
3638bb1310SDarrick J. Wong
3738bb1310SDarrick J. Wong error = xchk_trans_alloc(sc, 0);
3838bb1310SDarrick J. Wong if (error)
3938bb1310SDarrick J. Wong return error;
4038bb1310SDarrick J. Wong
41294012fbSDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL);
4238bb1310SDarrick J. Wong return 0;
4338bb1310SDarrick J. Wong }
4438bb1310SDarrick J. Wong
4538bb1310SDarrick J. Wong /* Install this scrub-by-handle inode and prepare it for scrubbing. */
4638bb1310SDarrick J. Wong static inline int
xchk_install_handle_iscrub(struct xfs_scrub * sc,struct xfs_inode * ip)4738bb1310SDarrick J. Wong xchk_install_handle_iscrub(
4838bb1310SDarrick J. Wong struct xfs_scrub *sc,
4938bb1310SDarrick J. Wong struct xfs_inode *ip)
5038bb1310SDarrick J. Wong {
5138bb1310SDarrick J. Wong int error;
5238bb1310SDarrick J. Wong
5338bb1310SDarrick J. Wong error = xchk_install_handle_inode(sc, ip);
5438bb1310SDarrick J. Wong if (error)
5538bb1310SDarrick J. Wong return error;
5638bb1310SDarrick J. Wong
5738bb1310SDarrick J. Wong return xchk_prepare_iscrub(sc);
5838bb1310SDarrick J. Wong }
5980e4e126SDarrick J. Wong
6080e4e126SDarrick J. Wong /*
6138bb1310SDarrick J. Wong * Grab total control of the inode metadata. In the best case, we grab the
6238bb1310SDarrick J. Wong * incore inode and take all locks on it. If the incore inode cannot be
6338bb1310SDarrick J. Wong * constructed due to corruption problems, lock the AGI so that we can single
6438bb1310SDarrick J. Wong * step the loading process to fix everything that can go wrong.
6580e4e126SDarrick J. Wong */
6680e4e126SDarrick J. Wong int
xchk_setup_inode(struct xfs_scrub * sc)67c517b3aaSDarrick J. Wong xchk_setup_inode(
68026f57ebSDarrick J. Wong struct xfs_scrub *sc)
6980e4e126SDarrick J. Wong {
7038bb1310SDarrick J. Wong struct xfs_imap imap;
7138bb1310SDarrick J. Wong struct xfs_inode *ip;
7238bb1310SDarrick J. Wong struct xfs_mount *mp = sc->mp;
7338bb1310SDarrick J. Wong struct xfs_inode *ip_in = XFS_I(file_inode(sc->file));
7438bb1310SDarrick J. Wong struct xfs_buf *agi_bp;
7538bb1310SDarrick J. Wong struct xfs_perag *pag;
7638bb1310SDarrick J. Wong xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, sc->sm->sm_ino);
7780e4e126SDarrick J. Wong int error;
7880e4e126SDarrick J. Wong
79466c525dSDarrick J. Wong if (xchk_need_intent_drain(sc))
80466c525dSDarrick J. Wong xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
81466c525dSDarrick J. Wong
8238bb1310SDarrick J. Wong /* We want to scan the opened inode, so lock it and exit. */
8338bb1310SDarrick J. Wong if (sc->sm->sm_ino == 0 || sc->sm->sm_ino == ip_in->i_ino) {
8417308539SDarrick J. Wong error = xchk_install_live_inode(sc, ip_in);
8517308539SDarrick J. Wong if (error)
8617308539SDarrick J. Wong return error;
8717308539SDarrick J. Wong
8838bb1310SDarrick J. Wong return xchk_prepare_iscrub(sc);
8980e4e126SDarrick J. Wong }
9080e4e126SDarrick J. Wong
9138bb1310SDarrick J. Wong /* Reject internal metadata files and obviously bad inode numbers. */
9238bb1310SDarrick J. Wong if (xfs_internal_inum(mp, sc->sm->sm_ino))
9338bb1310SDarrick J. Wong return -ENOENT;
9438bb1310SDarrick J. Wong if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
9538bb1310SDarrick J. Wong return -ENOENT;
9638bb1310SDarrick J. Wong
97fb6e584eSDarrick J. Wong /* Try a safe untrusted iget. */
98fb6e584eSDarrick J. Wong error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
9938bb1310SDarrick J. Wong if (!error)
10038bb1310SDarrick J. Wong return xchk_install_handle_iscrub(sc, ip);
10138bb1310SDarrick J. Wong if (error == -ENOENT)
10238bb1310SDarrick J. Wong return error;
10338bb1310SDarrick J. Wong if (error != -EFSCORRUPTED && error != -EFSBADCRC && error != -EINVAL)
10438bb1310SDarrick J. Wong goto out_error;
10538bb1310SDarrick J. Wong
10638bb1310SDarrick J. Wong /*
10738bb1310SDarrick J. Wong * EINVAL with IGET_UNTRUSTED probably means one of several things:
10838bb1310SDarrick J. Wong * userspace gave us an inode number that doesn't correspond to fs
10938bb1310SDarrick J. Wong * space; the inode btree lacks a record for this inode; or there is
11038bb1310SDarrick J. Wong * a record, and it says this inode is free.
11138bb1310SDarrick J. Wong *
11238bb1310SDarrick J. Wong * EFSCORRUPTED/EFSBADCRC could mean that the inode was mappable, but
11338bb1310SDarrick J. Wong * some other metadata corruption (e.g. inode forks) prevented
11438bb1310SDarrick J. Wong * instantiation of the incore inode. Or it could mean the inobt is
11538bb1310SDarrick J. Wong * corrupt.
11638bb1310SDarrick J. Wong *
11738bb1310SDarrick J. Wong * We want to look up this inode in the inobt directly to distinguish
11838bb1310SDarrick J. Wong * three different scenarios: (1) the inobt says the inode is free,
11938bb1310SDarrick J. Wong * in which case there's nothing to do; (2) the inobt is corrupt so we
12038bb1310SDarrick J. Wong * should flag the corruption and exit to userspace to let it fix the
12138bb1310SDarrick J. Wong * inobt; and (3) the inobt says the inode is allocated, but loading it
12238bb1310SDarrick J. Wong * failed due to corruption.
12338bb1310SDarrick J. Wong *
12438bb1310SDarrick J. Wong * Allocate a transaction and grab the AGI to prevent inobt activity in
12538bb1310SDarrick J. Wong * this AG. Retry the iget in case someone allocated a new inode after
12638bb1310SDarrick J. Wong * the first iget failed.
12738bb1310SDarrick J. Wong */
128c517b3aaSDarrick J. Wong error = xchk_trans_alloc(sc, 0);
12980e4e126SDarrick J. Wong if (error)
13038bb1310SDarrick J. Wong goto out_error;
13180e4e126SDarrick J. Wong
13238bb1310SDarrick J. Wong error = xchk_iget_agi(sc, sc->sm->sm_ino, &agi_bp, &ip);
13338bb1310SDarrick J. Wong if (error == 0) {
13438bb1310SDarrick J. Wong /* Actually got the incore inode, so install it and proceed. */
13538bb1310SDarrick J. Wong xchk_trans_cancel(sc);
13638bb1310SDarrick J. Wong return xchk_install_handle_iscrub(sc, ip);
13738bb1310SDarrick J. Wong }
13838bb1310SDarrick J. Wong if (error == -ENOENT)
13938bb1310SDarrick J. Wong goto out_gone;
14038bb1310SDarrick J. Wong if (error != -EFSCORRUPTED && error != -EFSBADCRC && error != -EINVAL)
14138bb1310SDarrick J. Wong goto out_cancel;
14238bb1310SDarrick J. Wong
14338bb1310SDarrick J. Wong /* Ensure that we have protected against inode allocation/freeing. */
14438bb1310SDarrick J. Wong if (agi_bp == NULL) {
14538bb1310SDarrick J. Wong ASSERT(agi_bp != NULL);
14638bb1310SDarrick J. Wong error = -ECANCELED;
14738bb1310SDarrick J. Wong goto out_cancel;
14838bb1310SDarrick J. Wong }
14938bb1310SDarrick J. Wong
15038bb1310SDarrick J. Wong /*
15138bb1310SDarrick J. Wong * Untrusted iget failed a second time. Let's try an inobt lookup.
15238bb1310SDarrick J. Wong * If the inobt doesn't think this is an allocated inode then we'll
15338bb1310SDarrick J. Wong * return ENOENT to signal that the check can be skipped.
15438bb1310SDarrick J. Wong *
15538bb1310SDarrick J. Wong * If the lookup signals corruption, we'll mark this inode corrupt and
15638bb1310SDarrick J. Wong * exit to userspace. There's little chance of fixing anything until
15738bb1310SDarrick J. Wong * the inobt is straightened out, but there's nothing we can do here.
15838bb1310SDarrick J. Wong *
15938bb1310SDarrick J. Wong * If the lookup encounters a runtime error, exit to userspace.
16038bb1310SDarrick J. Wong */
16138bb1310SDarrick J. Wong pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, sc->sm->sm_ino));
16238bb1310SDarrick J. Wong if (!pag) {
16338bb1310SDarrick J. Wong error = -EFSCORRUPTED;
16438bb1310SDarrick J. Wong goto out_cancel;
16538bb1310SDarrick J. Wong }
16638bb1310SDarrick J. Wong
16738bb1310SDarrick J. Wong error = xfs_imap(pag, sc->tp, sc->sm->sm_ino, &imap,
16838bb1310SDarrick J. Wong XFS_IGET_UNTRUSTED);
16938bb1310SDarrick J. Wong xfs_perag_put(pag);
17038bb1310SDarrick J. Wong if (error == -EINVAL || error == -ENOENT)
17138bb1310SDarrick J. Wong goto out_gone;
17238bb1310SDarrick J. Wong if (error)
17338bb1310SDarrick J. Wong goto out_cancel;
17438bb1310SDarrick J. Wong
17538bb1310SDarrick J. Wong /*
17638bb1310SDarrick J. Wong * The lookup succeeded. Chances are the ondisk inode is corrupt and
17738bb1310SDarrick J. Wong * preventing iget from reading it. Retain the scrub transaction and
17838bb1310SDarrick J. Wong * the AGI buffer to prevent anyone from allocating or freeing inodes.
17938bb1310SDarrick J. Wong * This ensures that we preserve the inconsistency between the inobt
18038bb1310SDarrick J. Wong * saying the inode is allocated and the icache being unable to load
18138bb1310SDarrick J. Wong * the inode until we can flag the corruption in xchk_inode. The
18238bb1310SDarrick J. Wong * scrub function has to note the corruption, since we're not really
18338bb1310SDarrick J. Wong * supposed to do that from the setup function.
18438bb1310SDarrick J. Wong */
18538bb1310SDarrick J. Wong return 0;
18638bb1310SDarrick J. Wong
18738bb1310SDarrick J. Wong out_cancel:
18838bb1310SDarrick J. Wong xchk_trans_cancel(sc);
18938bb1310SDarrick J. Wong out_error:
19038bb1310SDarrick J. Wong trace_xchk_op_error(sc, agno, XFS_INO_TO_AGBNO(mp, sc->sm->sm_ino),
19138bb1310SDarrick J. Wong error, __return_address);
19280e4e126SDarrick J. Wong return error;
19338bb1310SDarrick J. Wong out_gone:
19438bb1310SDarrick J. Wong /* The file is gone, so there's nothing to check. */
19538bb1310SDarrick J. Wong xchk_trans_cancel(sc);
19638bb1310SDarrick J. Wong return -ENOENT;
19780e4e126SDarrick J. Wong }
19880e4e126SDarrick J. Wong
19980e4e126SDarrick J. Wong /* Inode core */
20080e4e126SDarrick J. Wong
2018bb82bc1SDarrick J. Wong /* Validate di_extsize hint. */
20280e4e126SDarrick J. Wong STATIC void
xchk_inode_extsize(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags)203c517b3aaSDarrick J. Wong xchk_inode_extsize(
2041d8a748aSDarrick J. Wong struct xfs_scrub *sc,
20580e4e126SDarrick J. Wong struct xfs_dinode *dip,
20680e4e126SDarrick J. Wong xfs_ino_t ino,
20780e4e126SDarrick J. Wong uint16_t mode,
20880e4e126SDarrick J. Wong uint16_t flags)
20980e4e126SDarrick J. Wong {
2108bb82bc1SDarrick J. Wong xfs_failaddr_t fa;
211b102a46cSDarrick J. Wong uint32_t value = be32_to_cpu(dip->di_extsize);
21280e4e126SDarrick J. Wong
213b102a46cSDarrick J. Wong fa = xfs_inode_validate_extsize(sc->mp, value, mode, flags);
2148bb82bc1SDarrick J. Wong if (fa)
215c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
216b102a46cSDarrick J. Wong
217b102a46cSDarrick J. Wong /*
218b102a46cSDarrick J. Wong * XFS allows a sysadmin to change the rt extent size when adding a rt
219b102a46cSDarrick J. Wong * section to a filesystem after formatting. If there are any
220b102a46cSDarrick J. Wong * directories with extszinherit and rtinherit set, the hint could
221b102a46cSDarrick J. Wong * become misaligned with the new rextsize. The verifier doesn't check
222b102a46cSDarrick J. Wong * this, because we allow rtinherit directories even without an rt
223b102a46cSDarrick J. Wong * device. Flag this as an administrative warning since we will clean
224b102a46cSDarrick J. Wong * this up eventually.
225b102a46cSDarrick J. Wong */
226b102a46cSDarrick J. Wong if ((flags & XFS_DIFLAG_RTINHERIT) &&
227b102a46cSDarrick J. Wong (flags & XFS_DIFLAG_EXTSZINHERIT) &&
228b102a46cSDarrick J. Wong value % sc->mp->m_sb.sb_rextsize > 0)
229b102a46cSDarrick J. Wong xchk_ino_set_warning(sc, ino);
23080e4e126SDarrick J. Wong }
23180e4e126SDarrick J. Wong
23280e4e126SDarrick J. Wong /*
23380e4e126SDarrick J. Wong * Validate di_cowextsize hint.
23480e4e126SDarrick J. Wong *
23580e4e126SDarrick J. Wong * The rules are documented at xfs_ioctl_setattr_check_cowextsize().
23680e4e126SDarrick J. Wong * These functions must be kept in sync with each other.
23780e4e126SDarrick J. Wong */
23880e4e126SDarrick J. Wong STATIC void
xchk_inode_cowextsize(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags,uint64_t flags2)239c517b3aaSDarrick J. Wong xchk_inode_cowextsize(
2401d8a748aSDarrick J. Wong struct xfs_scrub *sc,
24180e4e126SDarrick J. Wong struct xfs_dinode *dip,
24280e4e126SDarrick J. Wong xfs_ino_t ino,
24380e4e126SDarrick J. Wong uint16_t mode,
24480e4e126SDarrick J. Wong uint16_t flags,
24580e4e126SDarrick J. Wong uint64_t flags2)
24680e4e126SDarrick J. Wong {
2478bb82bc1SDarrick J. Wong xfs_failaddr_t fa;
24880e4e126SDarrick J. Wong
2498bb82bc1SDarrick J. Wong fa = xfs_inode_validate_cowextsize(sc->mp,
2508bb82bc1SDarrick J. Wong be32_to_cpu(dip->di_cowextsize), mode, flags,
2518bb82bc1SDarrick J. Wong flags2);
2528bb82bc1SDarrick J. Wong if (fa)
253c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
25480e4e126SDarrick J. Wong }
25580e4e126SDarrick J. Wong
25680e4e126SDarrick J. Wong /* Make sure the di_flags make sense for the inode. */
25780e4e126SDarrick J. Wong STATIC void
xchk_inode_flags(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags)258c517b3aaSDarrick J. Wong xchk_inode_flags(
2591d8a748aSDarrick J. Wong struct xfs_scrub *sc,
26080e4e126SDarrick J. Wong struct xfs_dinode *dip,
26180e4e126SDarrick J. Wong xfs_ino_t ino,
26280e4e126SDarrick J. Wong uint16_t mode,
26380e4e126SDarrick J. Wong uint16_t flags)
26480e4e126SDarrick J. Wong {
26580e4e126SDarrick J. Wong struct xfs_mount *mp = sc->mp;
26680e4e126SDarrick J. Wong
267f369a13cSEric Sandeen /* di_flags are all taken, last bit cannot be used */
26880e4e126SDarrick J. Wong if (flags & ~XFS_DIFLAG_ANY)
26980e4e126SDarrick J. Wong goto bad;
27080e4e126SDarrick J. Wong
27180e4e126SDarrick J. Wong /* rt flags require rt device */
272c1f6b1acSDarrick J. Wong if ((flags & XFS_DIFLAG_REALTIME) && !mp->m_rtdev_targp)
27380e4e126SDarrick J. Wong goto bad;
27480e4e126SDarrick J. Wong
27580e4e126SDarrick J. Wong /* new rt bitmap flag only valid for rbmino */
27680e4e126SDarrick J. Wong if ((flags & XFS_DIFLAG_NEWRTBM) && ino != mp->m_sb.sb_rbmino)
27780e4e126SDarrick J. Wong goto bad;
27880e4e126SDarrick J. Wong
27980e4e126SDarrick J. Wong /* directory-only flags */
28080e4e126SDarrick J. Wong if ((flags & (XFS_DIFLAG_RTINHERIT |
28180e4e126SDarrick J. Wong XFS_DIFLAG_EXTSZINHERIT |
28280e4e126SDarrick J. Wong XFS_DIFLAG_PROJINHERIT |
28380e4e126SDarrick J. Wong XFS_DIFLAG_NOSYMLINKS)) &&
28480e4e126SDarrick J. Wong !S_ISDIR(mode))
28580e4e126SDarrick J. Wong goto bad;
28680e4e126SDarrick J. Wong
28780e4e126SDarrick J. Wong /* file-only flags */
28880e4e126SDarrick J. Wong if ((flags & (XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE)) &&
28980e4e126SDarrick J. Wong !S_ISREG(mode))
29080e4e126SDarrick J. Wong goto bad;
29180e4e126SDarrick J. Wong
29280e4e126SDarrick J. Wong /* filestreams and rt make no sense */
29380e4e126SDarrick J. Wong if ((flags & XFS_DIFLAG_FILESTREAM) && (flags & XFS_DIFLAG_REALTIME))
29480e4e126SDarrick J. Wong goto bad;
29580e4e126SDarrick J. Wong
29680e4e126SDarrick J. Wong return;
29780e4e126SDarrick J. Wong bad:
298c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
29980e4e126SDarrick J. Wong }
30080e4e126SDarrick J. Wong
30180e4e126SDarrick J. Wong /* Make sure the di_flags2 make sense for the inode. */
30280e4e126SDarrick J. Wong STATIC void
xchk_inode_flags2(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags,uint64_t flags2)303c517b3aaSDarrick J. Wong xchk_inode_flags2(
3041d8a748aSDarrick J. Wong struct xfs_scrub *sc,
30580e4e126SDarrick J. Wong struct xfs_dinode *dip,
30680e4e126SDarrick J. Wong xfs_ino_t ino,
30780e4e126SDarrick J. Wong uint16_t mode,
30880e4e126SDarrick J. Wong uint16_t flags,
30980e4e126SDarrick J. Wong uint64_t flags2)
31080e4e126SDarrick J. Wong {
31180e4e126SDarrick J. Wong struct xfs_mount *mp = sc->mp;
31280e4e126SDarrick J. Wong
313f369a13cSEric Sandeen /* Unknown di_flags2 could be from a future kernel */
31480e4e126SDarrick J. Wong if (flags2 & ~XFS_DIFLAG2_ANY)
315f369a13cSEric Sandeen xchk_ino_set_warning(sc, ino);
31680e4e126SDarrick J. Wong
31780e4e126SDarrick J. Wong /* reflink flag requires reflink feature */
31880e4e126SDarrick J. Wong if ((flags2 & XFS_DIFLAG2_REFLINK) &&
31938c26bfdSDave Chinner !xfs_has_reflink(mp))
32080e4e126SDarrick J. Wong goto bad;
32180e4e126SDarrick J. Wong
32280e4e126SDarrick J. Wong /* cowextsize flag is checked w.r.t. mode separately */
32380e4e126SDarrick J. Wong
32480e4e126SDarrick J. Wong /* file/dir-only flags */
32580e4e126SDarrick J. Wong if ((flags2 & XFS_DIFLAG2_DAX) && !(S_ISREG(mode) || S_ISDIR(mode)))
32680e4e126SDarrick J. Wong goto bad;
32780e4e126SDarrick J. Wong
32880e4e126SDarrick J. Wong /* file-only flags */
32980e4e126SDarrick J. Wong if ((flags2 & XFS_DIFLAG2_REFLINK) && !S_ISREG(mode))
33080e4e126SDarrick J. Wong goto bad;
33180e4e126SDarrick J. Wong
33280e4e126SDarrick J. Wong /* realtime and reflink make no sense, currently */
33380e4e126SDarrick J. Wong if ((flags & XFS_DIFLAG_REALTIME) && (flags2 & XFS_DIFLAG2_REFLINK))
33480e4e126SDarrick J. Wong goto bad;
33580e4e126SDarrick J. Wong
336f93e5436SDarrick J. Wong /* no bigtime iflag without the bigtime feature */
337ebd9027dSDave Chinner if (xfs_dinode_has_bigtime(dip) && !xfs_has_bigtime(mp))
338f93e5436SDarrick J. Wong goto bad;
339f93e5436SDarrick J. Wong
340*b9358db0SDarrick J. Wong /* no large extent counts without the filesystem feature */
341*b9358db0SDarrick J. Wong if ((flags2 & XFS_DIFLAG2_NREXT64) && !xfs_has_large_extent_counts(mp))
342*b9358db0SDarrick J. Wong goto bad;
343*b9358db0SDarrick J. Wong
34480e4e126SDarrick J. Wong return;
34580e4e126SDarrick J. Wong bad:
346c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
34780e4e126SDarrick J. Wong }
34880e4e126SDarrick J. Wong
3495a0bb066SDarrick J. Wong static inline void
xchk_dinode_nsec(struct xfs_scrub * sc,xfs_ino_t ino,struct xfs_dinode * dip,const xfs_timestamp_t ts)3505a0bb066SDarrick J. Wong xchk_dinode_nsec(
3515a0bb066SDarrick J. Wong struct xfs_scrub *sc,
3525a0bb066SDarrick J. Wong xfs_ino_t ino,
353f93e5436SDarrick J. Wong struct xfs_dinode *dip,
3545a0bb066SDarrick J. Wong const xfs_timestamp_t ts)
3555a0bb066SDarrick J. Wong {
3565a0bb066SDarrick J. Wong struct timespec64 tv;
3575a0bb066SDarrick J. Wong
358f93e5436SDarrick J. Wong tv = xfs_inode_from_disk_ts(dip, ts);
3595a0bb066SDarrick J. Wong if (tv.tv_nsec < 0 || tv.tv_nsec >= NSEC_PER_SEC)
3605a0bb066SDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
3615a0bb066SDarrick J. Wong }
3625a0bb066SDarrick J. Wong
36380e4e126SDarrick J. Wong /* Scrub all the ondisk inode fields. */
36480e4e126SDarrick J. Wong STATIC void
xchk_dinode(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino)365c517b3aaSDarrick J. Wong xchk_dinode(
3661d8a748aSDarrick J. Wong struct xfs_scrub *sc,
36780e4e126SDarrick J. Wong struct xfs_dinode *dip,
36880e4e126SDarrick J. Wong xfs_ino_t ino)
36980e4e126SDarrick J. Wong {
37080e4e126SDarrick J. Wong struct xfs_mount *mp = sc->mp;
37180e4e126SDarrick J. Wong size_t fork_recs;
37280e4e126SDarrick J. Wong unsigned long long isize;
37380e4e126SDarrick J. Wong uint64_t flags2;
374bb1d5049SChandan Babu R xfs_extnum_t nextents;
375dd95a6ceSChandan Babu R xfs_extnum_t naextents;
3767e937bb3SDarrick J. Wong prid_t prid;
37780e4e126SDarrick J. Wong uint16_t flags;
37880e4e126SDarrick J. Wong uint16_t mode;
37980e4e126SDarrick J. Wong
38080e4e126SDarrick J. Wong flags = be16_to_cpu(dip->di_flags);
38180e4e126SDarrick J. Wong if (dip->di_version >= 3)
38280e4e126SDarrick J. Wong flags2 = be64_to_cpu(dip->di_flags2);
38380e4e126SDarrick J. Wong else
38480e4e126SDarrick J. Wong flags2 = 0;
38580e4e126SDarrick J. Wong
38680e4e126SDarrick J. Wong /* di_mode */
38780e4e126SDarrick J. Wong mode = be16_to_cpu(dip->di_mode);
3883b42d385SDarrick J. Wong switch (mode & S_IFMT) {
3893b42d385SDarrick J. Wong case S_IFLNK:
3903b42d385SDarrick J. Wong case S_IFREG:
3913b42d385SDarrick J. Wong case S_IFDIR:
3923b42d385SDarrick J. Wong case S_IFCHR:
3933b42d385SDarrick J. Wong case S_IFBLK:
3943b42d385SDarrick J. Wong case S_IFIFO:
3953b42d385SDarrick J. Wong case S_IFSOCK:
3963b42d385SDarrick J. Wong /* mode is recognized */
3973b42d385SDarrick J. Wong break;
3983b42d385SDarrick J. Wong default:
399c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
4003b42d385SDarrick J. Wong break;
4013b42d385SDarrick J. Wong }
40280e4e126SDarrick J. Wong
40380e4e126SDarrick J. Wong /* v1/v2 fields */
40480e4e126SDarrick J. Wong switch (dip->di_version) {
40580e4e126SDarrick J. Wong case 1:
40680e4e126SDarrick J. Wong /*
40780e4e126SDarrick J. Wong * We autoconvert v1 inodes into v2 inodes on writeout,
40880e4e126SDarrick J. Wong * so just mark this inode for preening.
40980e4e126SDarrick J. Wong */
410c517b3aaSDarrick J. Wong xchk_ino_set_preen(sc, ino);
4117e937bb3SDarrick J. Wong prid = 0;
41280e4e126SDarrick J. Wong break;
41380e4e126SDarrick J. Wong case 2:
41480e4e126SDarrick J. Wong case 3:
41580e4e126SDarrick J. Wong if (dip->di_onlink != 0)
416c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
41780e4e126SDarrick J. Wong
41880e4e126SDarrick J. Wong if (dip->di_mode == 0 && sc->ip)
419c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
42080e4e126SDarrick J. Wong
42180e4e126SDarrick J. Wong if (dip->di_projid_hi != 0 &&
42238c26bfdSDave Chinner !xfs_has_projid32(mp))
423c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
4247e937bb3SDarrick J. Wong
4257e937bb3SDarrick J. Wong prid = be16_to_cpu(dip->di_projid_lo);
42680e4e126SDarrick J. Wong break;
42780e4e126SDarrick J. Wong default:
428c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
42980e4e126SDarrick J. Wong return;
43080e4e126SDarrick J. Wong }
43180e4e126SDarrick J. Wong
4327e937bb3SDarrick J. Wong if (xfs_has_projid32(mp))
4337e937bb3SDarrick J. Wong prid |= (prid_t)be16_to_cpu(dip->di_projid_hi) << 16;
4347e937bb3SDarrick J. Wong
43580e4e126SDarrick J. Wong /*
43680e4e126SDarrick J. Wong * di_uid/di_gid -- -1 isn't invalid, but there's no way that
43780e4e126SDarrick J. Wong * userspace could have created that.
43880e4e126SDarrick J. Wong */
43980e4e126SDarrick J. Wong if (dip->di_uid == cpu_to_be32(-1U) ||
44080e4e126SDarrick J. Wong dip->di_gid == cpu_to_be32(-1U))
441c517b3aaSDarrick J. Wong xchk_ino_set_warning(sc, ino);
44280e4e126SDarrick J. Wong
4437e937bb3SDarrick J. Wong /*
4447e937bb3SDarrick J. Wong * project id of -1 isn't supposed to be valid, but the kernel didn't
4457e937bb3SDarrick J. Wong * always validate that.
4467e937bb3SDarrick J. Wong */
4477e937bb3SDarrick J. Wong if (prid == -1U)
4487e937bb3SDarrick J. Wong xchk_ino_set_warning(sc, ino);
4497e937bb3SDarrick J. Wong
45080e4e126SDarrick J. Wong /* di_format */
45180e4e126SDarrick J. Wong switch (dip->di_format) {
45280e4e126SDarrick J. Wong case XFS_DINODE_FMT_DEV:
45380e4e126SDarrick J. Wong if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
45480e4e126SDarrick J. Wong !S_ISFIFO(mode) && !S_ISSOCK(mode))
455c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
45680e4e126SDarrick J. Wong break;
45780e4e126SDarrick J. Wong case XFS_DINODE_FMT_LOCAL:
45880e4e126SDarrick J. Wong if (!S_ISDIR(mode) && !S_ISLNK(mode))
459c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
46080e4e126SDarrick J. Wong break;
46180e4e126SDarrick J. Wong case XFS_DINODE_FMT_EXTENTS:
46280e4e126SDarrick J. Wong if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
463c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
46480e4e126SDarrick J. Wong break;
46580e4e126SDarrick J. Wong case XFS_DINODE_FMT_BTREE:
46680e4e126SDarrick J. Wong if (!S_ISREG(mode) && !S_ISDIR(mode))
467c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
46880e4e126SDarrick J. Wong break;
46980e4e126SDarrick J. Wong case XFS_DINODE_FMT_UUID:
47080e4e126SDarrick J. Wong default:
471c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
47280e4e126SDarrick J. Wong break;
47380e4e126SDarrick J. Wong }
47480e4e126SDarrick J. Wong
47529c1c123SDarrick J. Wong /* di_[amc]time.nsec */
476f93e5436SDarrick J. Wong xchk_dinode_nsec(sc, ino, dip, dip->di_atime);
477f93e5436SDarrick J. Wong xchk_dinode_nsec(sc, ino, dip, dip->di_mtime);
478f93e5436SDarrick J. Wong xchk_dinode_nsec(sc, ino, dip, dip->di_ctime);
47929c1c123SDarrick J. Wong
48080e4e126SDarrick J. Wong /*
48180e4e126SDarrick J. Wong * di_size. xfs_dinode_verify checks for things that screw up
48280e4e126SDarrick J. Wong * the VFS such as the upper bit being set and zero-length
48380e4e126SDarrick J. Wong * symlinks/directories, but we can do more here.
48480e4e126SDarrick J. Wong */
48580e4e126SDarrick J. Wong isize = be64_to_cpu(dip->di_size);
48680e4e126SDarrick J. Wong if (isize & (1ULL << 63))
487c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
48880e4e126SDarrick J. Wong
48980e4e126SDarrick J. Wong /* Devices, fifos, and sockets must have zero size */
49080e4e126SDarrick J. Wong if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
491c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
49280e4e126SDarrick J. Wong
49380e4e126SDarrick J. Wong /* Directories can't be larger than the data section size (32G) */
49480e4e126SDarrick J. Wong if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
495c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
49680e4e126SDarrick J. Wong
49780e4e126SDarrick J. Wong /* Symlinks can't be larger than SYMLINK_MAXLEN */
49880e4e126SDarrick J. Wong if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
499c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
50080e4e126SDarrick J. Wong
50180e4e126SDarrick J. Wong /*
50280e4e126SDarrick J. Wong * Warn if the running kernel can't handle the kinds of offsets
50380e4e126SDarrick J. Wong * needed to deal with the file size. In other words, if the
50480e4e126SDarrick J. Wong * pagecache can't cache all the blocks in this file due to
50580e4e126SDarrick J. Wong * overly large offsets, flag the inode for admin review.
50680e4e126SDarrick J. Wong */
507bd5ab5f9SDarrick J. Wong if (isize > mp->m_super->s_maxbytes)
508c517b3aaSDarrick J. Wong xchk_ino_set_warning(sc, ino);
50980e4e126SDarrick J. Wong
51080e4e126SDarrick J. Wong /* di_nblocks */
51180e4e126SDarrick J. Wong if (flags2 & XFS_DIFLAG2_REFLINK) {
51280e4e126SDarrick J. Wong ; /* nblocks can exceed dblocks */
51380e4e126SDarrick J. Wong } else if (flags & XFS_DIFLAG_REALTIME) {
51480e4e126SDarrick J. Wong /*
51580e4e126SDarrick J. Wong * nblocks is the sum of data extents (in the rtdev),
51680e4e126SDarrick J. Wong * attr extents (in the datadev), and both forks' bmbt
51780e4e126SDarrick J. Wong * blocks (in the datadev). This clumsy check is the
51880e4e126SDarrick J. Wong * best we can do without cross-referencing with the
51980e4e126SDarrick J. Wong * inode forks.
52080e4e126SDarrick J. Wong */
52180e4e126SDarrick J. Wong if (be64_to_cpu(dip->di_nblocks) >=
52280e4e126SDarrick J. Wong mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
523c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
52480e4e126SDarrick J. Wong } else {
52580e4e126SDarrick J. Wong if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
526c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
52780e4e126SDarrick J. Wong }
52880e4e126SDarrick J. Wong
529c517b3aaSDarrick J. Wong xchk_inode_flags(sc, dip, ino, mode, flags);
53080e4e126SDarrick J. Wong
531c517b3aaSDarrick J. Wong xchk_inode_extsize(sc, dip, ino, mode, flags);
53280e4e126SDarrick J. Wong
533dd95a6ceSChandan Babu R nextents = xfs_dfork_data_extents(dip);
534dd95a6ceSChandan Babu R naextents = xfs_dfork_attr_extents(dip);
535dd95a6ceSChandan Babu R
53680e4e126SDarrick J. Wong /* di_nextents */
53780e4e126SDarrick J. Wong fork_recs = XFS_DFORK_DSIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
53880e4e126SDarrick J. Wong switch (dip->di_format) {
53980e4e126SDarrick J. Wong case XFS_DINODE_FMT_EXTENTS:
54080e4e126SDarrick J. Wong if (nextents > fork_recs)
541c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
54280e4e126SDarrick J. Wong break;
54380e4e126SDarrick J. Wong case XFS_DINODE_FMT_BTREE:
54480e4e126SDarrick J. Wong if (nextents <= fork_recs)
545c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
54680e4e126SDarrick J. Wong break;
54780e4e126SDarrick J. Wong default:
54880e4e126SDarrick J. Wong if (nextents != 0)
549c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
55080e4e126SDarrick J. Wong break;
55180e4e126SDarrick J. Wong }
55280e4e126SDarrick J. Wong
55380e4e126SDarrick J. Wong /* di_forkoff */
55480e4e126SDarrick J. Wong if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
555c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
556dd95a6ceSChandan Babu R if (naextents != 0 && dip->di_forkoff == 0)
557c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
55880e4e126SDarrick J. Wong if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
559c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
56080e4e126SDarrick J. Wong
56180e4e126SDarrick J. Wong /* di_aformat */
56280e4e126SDarrick J. Wong if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
56380e4e126SDarrick J. Wong dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
56480e4e126SDarrick J. Wong dip->di_aformat != XFS_DINODE_FMT_BTREE)
565c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
56680e4e126SDarrick J. Wong
56780e4e126SDarrick J. Wong /* di_anextents */
56880e4e126SDarrick J. Wong fork_recs = XFS_DFORK_ASIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
56980e4e126SDarrick J. Wong switch (dip->di_aformat) {
57080e4e126SDarrick J. Wong case XFS_DINODE_FMT_EXTENTS:
571dd95a6ceSChandan Babu R if (naextents > fork_recs)
572c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
57380e4e126SDarrick J. Wong break;
57480e4e126SDarrick J. Wong case XFS_DINODE_FMT_BTREE:
575dd95a6ceSChandan Babu R if (naextents <= fork_recs)
576c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
57780e4e126SDarrick J. Wong break;
57880e4e126SDarrick J. Wong default:
579dd95a6ceSChandan Babu R if (naextents != 0)
580c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
58180e4e126SDarrick J. Wong }
58280e4e126SDarrick J. Wong
58380e4e126SDarrick J. Wong if (dip->di_version >= 3) {
584f93e5436SDarrick J. Wong xchk_dinode_nsec(sc, ino, dip, dip->di_crtime);
585c517b3aaSDarrick J. Wong xchk_inode_flags2(sc, dip, ino, mode, flags, flags2);
586c517b3aaSDarrick J. Wong xchk_inode_cowextsize(sc, dip, ino, mode, flags,
58780e4e126SDarrick J. Wong flags2);
58880e4e126SDarrick J. Wong }
58980e4e126SDarrick J. Wong }
59080e4e126SDarrick J. Wong
5912e6f2756SDarrick J. Wong /*
5922e6f2756SDarrick J. Wong * Make sure the finobt doesn't think this inode is free.
5932e6f2756SDarrick J. Wong * We don't have to check the inobt ourselves because we got the inode via
5942e6f2756SDarrick J. Wong * IGET_UNTRUSTED, which checks the inobt for us.
5952e6f2756SDarrick J. Wong */
5962e6f2756SDarrick J. Wong static void
xchk_inode_xref_finobt(struct xfs_scrub * sc,xfs_ino_t ino)597c517b3aaSDarrick J. Wong xchk_inode_xref_finobt(
5981d8a748aSDarrick J. Wong struct xfs_scrub *sc,
5992e6f2756SDarrick J. Wong xfs_ino_t ino)
6002e6f2756SDarrick J. Wong {
6012e6f2756SDarrick J. Wong struct xfs_inobt_rec_incore rec;
6022e6f2756SDarrick J. Wong xfs_agino_t agino;
6032e6f2756SDarrick J. Wong int has_record;
6042e6f2756SDarrick J. Wong int error;
6052e6f2756SDarrick J. Wong
606c517b3aaSDarrick J. Wong if (!sc->sa.fino_cur || xchk_skip_xref(sc->sm))
6072e6f2756SDarrick J. Wong return;
6082e6f2756SDarrick J. Wong
6092e6f2756SDarrick J. Wong agino = XFS_INO_TO_AGINO(sc->mp, ino);
6102e6f2756SDarrick J. Wong
6112e6f2756SDarrick J. Wong /*
6122e6f2756SDarrick J. Wong * Try to get the finobt record. If we can't get it, then we're
6132e6f2756SDarrick J. Wong * in good shape.
6142e6f2756SDarrick J. Wong */
6152e6f2756SDarrick J. Wong error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE,
6162e6f2756SDarrick J. Wong &has_record);
617c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
6182e6f2756SDarrick J. Wong !has_record)
6192e6f2756SDarrick J. Wong return;
6202e6f2756SDarrick J. Wong
6212e6f2756SDarrick J. Wong error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record);
622c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
6232e6f2756SDarrick J. Wong !has_record)
6242e6f2756SDarrick J. Wong return;
6252e6f2756SDarrick J. Wong
6262e6f2756SDarrick J. Wong /*
6272e6f2756SDarrick J. Wong * Otherwise, make sure this record either doesn't cover this inode,
6282e6f2756SDarrick J. Wong * or that it does but it's marked present.
6292e6f2756SDarrick J. Wong */
6302e6f2756SDarrick J. Wong if (rec.ir_startino > agino ||
6312e6f2756SDarrick J. Wong rec.ir_startino + XFS_INODES_PER_CHUNK <= agino)
6322e6f2756SDarrick J. Wong return;
6332e6f2756SDarrick J. Wong
6342e6f2756SDarrick J. Wong if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))
635c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
6362e6f2756SDarrick J. Wong }
6372e6f2756SDarrick J. Wong
638561f648aSDarrick J. Wong /* Cross reference the inode fields with the forks. */
639561f648aSDarrick J. Wong STATIC void
xchk_inode_xref_bmap(struct xfs_scrub * sc,struct xfs_dinode * dip)640c517b3aaSDarrick J. Wong xchk_inode_xref_bmap(
6411d8a748aSDarrick J. Wong struct xfs_scrub *sc,
642561f648aSDarrick J. Wong struct xfs_dinode *dip)
643561f648aSDarrick J. Wong {
644561f648aSDarrick J. Wong xfs_extnum_t nextents;
645561f648aSDarrick J. Wong xfs_filblks_t count;
646561f648aSDarrick J. Wong xfs_filblks_t acount;
647561f648aSDarrick J. Wong int error;
648561f648aSDarrick J. Wong
649c517b3aaSDarrick J. Wong if (xchk_skip_xref(sc->sm))
6508389f3ffSDarrick J. Wong return;
6518389f3ffSDarrick J. Wong
652561f648aSDarrick J. Wong /* Walk all the extents to check nextents/naextents/nblocks. */
653561f648aSDarrick J. Wong error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
654561f648aSDarrick J. Wong &nextents, &count);
655c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, NULL))
656561f648aSDarrick J. Wong return;
657dd95a6ceSChandan Babu R if (nextents < xfs_dfork_data_extents(dip))
658c517b3aaSDarrick J. Wong xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
659561f648aSDarrick J. Wong
660561f648aSDarrick J. Wong error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
661561f648aSDarrick J. Wong &nextents, &acount);
662c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, NULL))
663561f648aSDarrick J. Wong return;
664dd95a6ceSChandan Babu R if (nextents != xfs_dfork_attr_extents(dip))
665c517b3aaSDarrick J. Wong xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
666561f648aSDarrick J. Wong
667561f648aSDarrick J. Wong /* Check nblocks against the inode. */
668561f648aSDarrick J. Wong if (count + acount != be64_to_cpu(dip->di_nblocks))
669c517b3aaSDarrick J. Wong xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
670561f648aSDarrick J. Wong }
671561f648aSDarrick J. Wong
672166d7641SDarrick J. Wong /* Cross-reference with the other btrees. */
673166d7641SDarrick J. Wong STATIC void
xchk_inode_xref(struct xfs_scrub * sc,xfs_ino_t ino,struct xfs_dinode * dip)674c517b3aaSDarrick J. Wong xchk_inode_xref(
6751d8a748aSDarrick J. Wong struct xfs_scrub *sc,
676166d7641SDarrick J. Wong xfs_ino_t ino,
677166d7641SDarrick J. Wong struct xfs_dinode *dip)
678166d7641SDarrick J. Wong {
67952dc4b44SDarrick J. Wong xfs_agnumber_t agno;
68052dc4b44SDarrick J. Wong xfs_agblock_t agbno;
68152dc4b44SDarrick J. Wong int error;
68252dc4b44SDarrick J. Wong
683166d7641SDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
684166d7641SDarrick J. Wong return;
68552dc4b44SDarrick J. Wong
68652dc4b44SDarrick J. Wong agno = XFS_INO_TO_AGNO(sc->mp, ino);
68752dc4b44SDarrick J. Wong agbno = XFS_INO_TO_AGBNO(sc->mp, ino);
68852dc4b44SDarrick J. Wong
68948c6615cSDarrick J. Wong error = xchk_ag_init_existing(sc, agno, &sc->sa);
690c517b3aaSDarrick J. Wong if (!xchk_xref_process_error(sc, agno, agbno, &error))
69161e0d0ccSDarrick J. Wong goto out_free;
69252dc4b44SDarrick J. Wong
693c517b3aaSDarrick J. Wong xchk_xref_is_used_space(sc, agbno, 1);
694c517b3aaSDarrick J. Wong xchk_inode_xref_finobt(sc, ino);
69569115f77SDarrick J. Wong xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
696c517b3aaSDarrick J. Wong xchk_xref_is_not_shared(sc, agbno, 1);
6977ac14fa2SDarrick J. Wong xchk_xref_is_not_cow_staging(sc, agbno, 1);
698c517b3aaSDarrick J. Wong xchk_inode_xref_bmap(sc, dip);
69952dc4b44SDarrick J. Wong
70061e0d0ccSDarrick J. Wong out_free:
701c517b3aaSDarrick J. Wong xchk_ag_free(sc, &sc->sa);
702166d7641SDarrick J. Wong }
703166d7641SDarrick J. Wong
704f6d5fc21SDarrick J. Wong /*
705f6d5fc21SDarrick J. Wong * If the reflink iflag disagrees with a scan for shared data fork extents,
706f6d5fc21SDarrick J. Wong * either flag an error (shared extents w/ no flag) or a preen (flag set w/o
707f6d5fc21SDarrick J. Wong * any shared extents). We already checked for reflink iflag set on a non
708f6d5fc21SDarrick J. Wong * reflink filesystem.
709f6d5fc21SDarrick J. Wong */
710f6d5fc21SDarrick J. Wong static void
xchk_inode_check_reflink_iflag(struct xfs_scrub * sc,xfs_ino_t ino)711c517b3aaSDarrick J. Wong xchk_inode_check_reflink_iflag(
7121d8a748aSDarrick J. Wong struct xfs_scrub *sc,
7137e56d9eaSDarrick J. Wong xfs_ino_t ino)
714f6d5fc21SDarrick J. Wong {
715f6d5fc21SDarrick J. Wong struct xfs_mount *mp = sc->mp;
716f6d5fc21SDarrick J. Wong bool has_shared;
717f6d5fc21SDarrick J. Wong int error;
718f6d5fc21SDarrick J. Wong
71938c26bfdSDave Chinner if (!xfs_has_reflink(mp))
720f6d5fc21SDarrick J. Wong return;
721f6d5fc21SDarrick J. Wong
722f6d5fc21SDarrick J. Wong error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
723f6d5fc21SDarrick J. Wong &has_shared);
724c517b3aaSDarrick J. Wong if (!xchk_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
725f6d5fc21SDarrick J. Wong XFS_INO_TO_AGBNO(mp, ino), &error))
726f6d5fc21SDarrick J. Wong return;
727f6d5fc21SDarrick J. Wong if (xfs_is_reflink_inode(sc->ip) && !has_shared)
728c517b3aaSDarrick J. Wong xchk_ino_set_preen(sc, ino);
729f6d5fc21SDarrick J. Wong else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
730c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, ino);
731f6d5fc21SDarrick J. Wong }
732f6d5fc21SDarrick J. Wong
73380e4e126SDarrick J. Wong /* Scrub an inode. */
73480e4e126SDarrick J. Wong int
xchk_inode(struct xfs_scrub * sc)735c517b3aaSDarrick J. Wong xchk_inode(
7361d8a748aSDarrick J. Wong struct xfs_scrub *sc)
73780e4e126SDarrick J. Wong {
73880e4e126SDarrick J. Wong struct xfs_dinode di;
73980e4e126SDarrick J. Wong int error = 0;
74080e4e126SDarrick J. Wong
741d0018ad8SDarrick J. Wong /*
742d0018ad8SDarrick J. Wong * If sc->ip is NULL, that means that the setup function called
743d0018ad8SDarrick J. Wong * xfs_iget to look up the inode. xfs_iget returned a EFSCORRUPTED
744d0018ad8SDarrick J. Wong * and a NULL inode, so flag the corruption error and return.
745d0018ad8SDarrick J. Wong */
746d0018ad8SDarrick J. Wong if (!sc->ip) {
747c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->sm->sm_ino);
748d0018ad8SDarrick J. Wong return 0;
74980e4e126SDarrick J. Wong }
75080e4e126SDarrick J. Wong
751d0018ad8SDarrick J. Wong /* Scrub the inode core. */
752d0018ad8SDarrick J. Wong xfs_inode_to_disk(sc->ip, &di, 0);
753c517b3aaSDarrick J. Wong xchk_dinode(sc, &di, sc->ip->i_ino);
75480e4e126SDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
75580e4e126SDarrick J. Wong goto out;
75680e4e126SDarrick J. Wong
75780e4e126SDarrick J. Wong /*
758f6d5fc21SDarrick J. Wong * Look for discrepancies between file's data blocks and the reflink
759f6d5fc21SDarrick J. Wong * iflag. We already checked the iflag against the file mode when
760f6d5fc21SDarrick J. Wong * we scrubbed the dinode.
76180e4e126SDarrick J. Wong */
762f6d5fc21SDarrick J. Wong if (S_ISREG(VFS_I(sc->ip)->i_mode))
763c517b3aaSDarrick J. Wong xchk_inode_check_reflink_iflag(sc, sc->ip->i_ino);
76480e4e126SDarrick J. Wong
765c517b3aaSDarrick J. Wong xchk_inode_xref(sc, sc->ip->i_ino, &di);
76680e4e126SDarrick J. Wong out:
76780e4e126SDarrick J. Wong return error;
76880e4e126SDarrick J. Wong }
769