blob: 2405b09d03d08f0c407c47c7a919559b5668b46f [file] [log] [blame]
Dave Chinner0b61f8a2018-06-05 19:42:14 -07001// SPDX-License-Identifier: GPL-2.0+
Darrick J. Wong80e4e122017-10-17 21:37:42 -07002/*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
Darrick J. Wong80e4e122017-10-17 21:37:42 -07004 * Author: Darrick J. Wong <darrick.wong@oracle.com>
Darrick J. Wong80e4e122017-10-17 21:37:42 -07005 */
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"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070012#include "xfs_btree.h"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070013#include "xfs_log_format.h"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070014#include "xfs_inode.h"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070015#include "xfs_ialloc.h"
16#include "xfs_da_format.h"
17#include "xfs_reflink.h"
Darrick J. Wongd8526572018-01-16 18:53:08 -080018#include "xfs_rmap.h"
Darrick J. Wong561f6482018-01-16 18:53:10 -080019#include "xfs_bmap_util.h"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070020#include "scrub/scrub.h"
21#include "scrub/common.h"
Darrick J. Wong2e6f2752018-01-16 18:53:07 -080022#include "scrub/btree.h"
Darrick J. Wong80e4e122017-10-17 21:37:42 -070023
24/*
25 * Grab total control of the inode metadata. It doesn't matter here if
26 * the file data is still changing; exclusive access to the metadata is
27 * the goal.
28 */
29int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070030xchk_setup_inode(
Darrick J. Wong026f57e2021-04-07 17:59:39 -070031 struct xfs_scrub *sc)
Darrick J. Wong80e4e122017-10-17 21:37:42 -070032{
Darrick J. Wong032d91f2018-07-19 12:29:12 -070033 int error;
Darrick J. Wong80e4e122017-10-17 21:37:42 -070034
35 /*
36 * Try to get the inode. If the verifiers fail, we try again
37 * in raw mode.
38 */
Darrick J. Wong026f57e2021-04-07 17:59:39 -070039 error = xchk_get_inode(sc);
Darrick J. Wong80e4e122017-10-17 21:37:42 -070040 switch (error) {
41 case 0:
42 break;
43 case -EFSCORRUPTED:
44 case -EFSBADCRC:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070045 return xchk_trans_alloc(sc, 0);
Darrick J. Wong80e4e122017-10-17 21:37:42 -070046 default:
47 return error;
48 }
49
50 /* Got the inode, lock it and we're ready to go. */
51 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
52 xfs_ilock(sc->ip, sc->ilock_flags);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070053 error = xchk_trans_alloc(sc, 0);
Darrick J. Wong80e4e122017-10-17 21:37:42 -070054 if (error)
55 goto out;
56 sc->ilock_flags |= XFS_ILOCK_EXCL;
57 xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
58
59out:
60 /* scrub teardown will unlock and release the inode for us */
61 return error;
62}
63
64/* Inode core */
65
Darrick J. Wong8bb82bc2018-03-23 10:06:55 -070066/* Validate di_extsize hint. */
Darrick J. Wong80e4e122017-10-17 21:37:42 -070067STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070068xchk_inode_extsize(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -070069 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -070070 struct xfs_dinode *dip,
71 xfs_ino_t ino,
72 uint16_t mode,
73 uint16_t flags)
Darrick J. Wong80e4e122017-10-17 21:37:42 -070074{
Darrick J. Wong032d91f2018-07-19 12:29:12 -070075 xfs_failaddr_t fa;
Darrick J. Wongb102a462021-07-14 09:03:41 -070076 uint32_t value = be32_to_cpu(dip->di_extsize);
Darrick J. Wong80e4e122017-10-17 21:37:42 -070077
Darrick J. Wongb102a462021-07-14 09:03:41 -070078 fa = xfs_inode_validate_extsize(sc->mp, value, mode, flags);
Darrick J. Wong8bb82bc2018-03-23 10:06:55 -070079 if (fa)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070080 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wongb102a462021-07-14 09:03:41 -070081
82 /*
83 * XFS allows a sysadmin to change the rt extent size when adding a rt
84 * section to a filesystem after formatting. If there are any
85 * directories with extszinherit and rtinherit set, the hint could
86 * become misaligned with the new rextsize. The verifier doesn't check
87 * this, because we allow rtinherit directories even without an rt
88 * device. Flag this as an administrative warning since we will clean
89 * this up eventually.
90 */
91 if ((flags & XFS_DIFLAG_RTINHERIT) &&
92 (flags & XFS_DIFLAG_EXTSZINHERIT) &&
93 value % sc->mp->m_sb.sb_rextsize > 0)
94 xchk_ino_set_warning(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -070095}
96
97/*
98 * Validate di_cowextsize hint.
99 *
100 * The rules are documented at xfs_ioctl_setattr_check_cowextsize().
101 * These functions must be kept in sync with each other.
102 */
103STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700104xchk_inode_cowextsize(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700105 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700106 struct xfs_dinode *dip,
107 xfs_ino_t ino,
108 uint16_t mode,
109 uint16_t flags,
110 uint64_t flags2)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700111{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700112 xfs_failaddr_t fa;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700113
Darrick J. Wong8bb82bc2018-03-23 10:06:55 -0700114 fa = xfs_inode_validate_cowextsize(sc->mp,
115 be32_to_cpu(dip->di_cowextsize), mode, flags,
116 flags2);
117 if (fa)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700118 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700119}
120
121/* Make sure the di_flags make sense for the inode. */
122STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700123xchk_inode_flags(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700124 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700125 struct xfs_dinode *dip,
126 xfs_ino_t ino,
127 uint16_t mode,
128 uint16_t flags)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700129{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700130 struct xfs_mount *mp = sc->mp;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700131
Eric Sandeenf369a132018-09-29 13:49:00 +1000132 /* di_flags are all taken, last bit cannot be used */
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700133 if (flags & ~XFS_DIFLAG_ANY)
134 goto bad;
135
136 /* rt flags require rt device */
Darrick J. Wongc1f6b1a2020-11-02 17:14:07 -0800137 if ((flags & XFS_DIFLAG_REALTIME) && !mp->m_rtdev_targp)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700138 goto bad;
139
140 /* new rt bitmap flag only valid for rbmino */
141 if ((flags & XFS_DIFLAG_NEWRTBM) && ino != mp->m_sb.sb_rbmino)
142 goto bad;
143
144 /* directory-only flags */
145 if ((flags & (XFS_DIFLAG_RTINHERIT |
146 XFS_DIFLAG_EXTSZINHERIT |
147 XFS_DIFLAG_PROJINHERIT |
148 XFS_DIFLAG_NOSYMLINKS)) &&
149 !S_ISDIR(mode))
150 goto bad;
151
152 /* file-only flags */
153 if ((flags & (XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE)) &&
154 !S_ISREG(mode))
155 goto bad;
156
157 /* filestreams and rt make no sense */
158 if ((flags & XFS_DIFLAG_FILESTREAM) && (flags & XFS_DIFLAG_REALTIME))
159 goto bad;
160
161 return;
162bad:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700163 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700164}
165
166/* Make sure the di_flags2 make sense for the inode. */
167STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700168xchk_inode_flags2(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700169 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700170 struct xfs_dinode *dip,
171 xfs_ino_t ino,
172 uint16_t mode,
173 uint16_t flags,
174 uint64_t flags2)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700175{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700176 struct xfs_mount *mp = sc->mp;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700177
Eric Sandeenf369a132018-09-29 13:49:00 +1000178 /* Unknown di_flags2 could be from a future kernel */
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700179 if (flags2 & ~XFS_DIFLAG2_ANY)
Eric Sandeenf369a132018-09-29 13:49:00 +1000180 xchk_ino_set_warning(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700181
182 /* reflink flag requires reflink feature */
183 if ((flags2 & XFS_DIFLAG2_REFLINK) &&
Dave Chinner38c26bf2021-08-18 18:46:37 -0700184 !xfs_has_reflink(mp))
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700185 goto bad;
186
187 /* cowextsize flag is checked w.r.t. mode separately */
188
189 /* file/dir-only flags */
190 if ((flags2 & XFS_DIFLAG2_DAX) && !(S_ISREG(mode) || S_ISDIR(mode)))
191 goto bad;
192
193 /* file-only flags */
194 if ((flags2 & XFS_DIFLAG2_REFLINK) && !S_ISREG(mode))
195 goto bad;
196
197 /* realtime and reflink make no sense, currently */
198 if ((flags & XFS_DIFLAG_REALTIME) && (flags2 & XFS_DIFLAG2_REFLINK))
199 goto bad;
200
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700201 /* no bigtime iflag without the bigtime feature */
Dave Chinnerebd90272021-08-18 18:46:55 -0700202 if (xfs_dinode_has_bigtime(dip) && !xfs_has_bigtime(mp))
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700203 goto bad;
204
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700205 return;
206bad:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700207 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700208}
209
Darrick J. Wong5a0bb062020-08-24 15:15:46 -0700210static inline void
211xchk_dinode_nsec(
212 struct xfs_scrub *sc,
213 xfs_ino_t ino,
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700214 struct xfs_dinode *dip,
Darrick J. Wong5a0bb062020-08-24 15:15:46 -0700215 const xfs_timestamp_t ts)
216{
217 struct timespec64 tv;
218
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700219 tv = xfs_inode_from_disk_ts(dip, ts);
Darrick J. Wong5a0bb062020-08-24 15:15:46 -0700220 if (tv.tv_nsec < 0 || tv.tv_nsec >= NSEC_PER_SEC)
221 xchk_ino_set_corrupt(sc, ino);
222}
223
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700224/* Scrub all the ondisk inode fields. */
225STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700226xchk_dinode(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700227 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700228 struct xfs_dinode *dip,
229 xfs_ino_t ino)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700230{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700231 struct xfs_mount *mp = sc->mp;
232 size_t fork_recs;
233 unsigned long long isize;
234 uint64_t flags2;
235 uint32_t nextents;
236 uint16_t flags;
237 uint16_t mode;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700238
239 flags = be16_to_cpu(dip->di_flags);
240 if (dip->di_version >= 3)
241 flags2 = be64_to_cpu(dip->di_flags2);
242 else
243 flags2 = 0;
244
245 /* di_mode */
246 mode = be16_to_cpu(dip->di_mode);
Darrick J. Wong3b42d382017-11-27 21:40:19 -0800247 switch (mode & S_IFMT) {
248 case S_IFLNK:
249 case S_IFREG:
250 case S_IFDIR:
251 case S_IFCHR:
252 case S_IFBLK:
253 case S_IFIFO:
254 case S_IFSOCK:
255 /* mode is recognized */
256 break;
257 default:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700258 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong3b42d382017-11-27 21:40:19 -0800259 break;
260 }
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700261
262 /* v1/v2 fields */
263 switch (dip->di_version) {
264 case 1:
265 /*
266 * We autoconvert v1 inodes into v2 inodes on writeout,
267 * so just mark this inode for preening.
268 */
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700269 xchk_ino_set_preen(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700270 break;
271 case 2:
272 case 3:
273 if (dip->di_onlink != 0)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700274 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700275
276 if (dip->di_mode == 0 && sc->ip)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700277 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700278
279 if (dip->di_projid_hi != 0 &&
Dave Chinner38c26bf2021-08-18 18:46:37 -0700280 !xfs_has_projid32(mp))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700281 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700282 break;
283 default:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700284 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700285 return;
286 }
287
288 /*
289 * di_uid/di_gid -- -1 isn't invalid, but there's no way that
290 * userspace could have created that.
291 */
292 if (dip->di_uid == cpu_to_be32(-1U) ||
293 dip->di_gid == cpu_to_be32(-1U))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700294 xchk_ino_set_warning(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700295
296 /* di_format */
297 switch (dip->di_format) {
298 case XFS_DINODE_FMT_DEV:
299 if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
300 !S_ISFIFO(mode) && !S_ISSOCK(mode))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700301 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700302 break;
303 case XFS_DINODE_FMT_LOCAL:
304 if (!S_ISDIR(mode) && !S_ISLNK(mode))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700305 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700306 break;
307 case XFS_DINODE_FMT_EXTENTS:
308 if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700309 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700310 break;
311 case XFS_DINODE_FMT_BTREE:
312 if (!S_ISREG(mode) && !S_ISDIR(mode))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700313 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700314 break;
315 case XFS_DINODE_FMT_UUID:
316 default:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700317 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700318 break;
319 }
320
Darrick J. Wong29c1c122018-01-08 10:41:35 -0800321 /* di_[amc]time.nsec */
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700322 xchk_dinode_nsec(sc, ino, dip, dip->di_atime);
323 xchk_dinode_nsec(sc, ino, dip, dip->di_mtime);
324 xchk_dinode_nsec(sc, ino, dip, dip->di_ctime);
Darrick J. Wong29c1c122018-01-08 10:41:35 -0800325
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700326 /*
327 * di_size. xfs_dinode_verify checks for things that screw up
328 * the VFS such as the upper bit being set and zero-length
329 * symlinks/directories, but we can do more here.
330 */
331 isize = be64_to_cpu(dip->di_size);
332 if (isize & (1ULL << 63))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700333 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700334
335 /* Devices, fifos, and sockets must have zero size */
336 if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700337 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700338
339 /* Directories can't be larger than the data section size (32G) */
340 if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700341 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700342
343 /* Symlinks can't be larger than SYMLINK_MAXLEN */
344 if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700345 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700346
347 /*
348 * Warn if the running kernel can't handle the kinds of offsets
349 * needed to deal with the file size. In other words, if the
350 * pagecache can't cache all the blocks in this file due to
351 * overly large offsets, flag the inode for admin review.
352 */
353 if (isize >= mp->m_super->s_maxbytes)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700354 xchk_ino_set_warning(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700355
356 /* di_nblocks */
357 if (flags2 & XFS_DIFLAG2_REFLINK) {
358 ; /* nblocks can exceed dblocks */
359 } else if (flags & XFS_DIFLAG_REALTIME) {
360 /*
361 * nblocks is the sum of data extents (in the rtdev),
362 * attr extents (in the datadev), and both forks' bmbt
363 * blocks (in the datadev). This clumsy check is the
364 * best we can do without cross-referencing with the
365 * inode forks.
366 */
367 if (be64_to_cpu(dip->di_nblocks) >=
368 mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700369 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700370 } else {
371 if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700372 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700373 }
374
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700375 xchk_inode_flags(sc, dip, ino, mode, flags);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700376
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700377 xchk_inode_extsize(sc, dip, ino, mode, flags);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700378
379 /* di_nextents */
380 nextents = be32_to_cpu(dip->di_nextents);
381 fork_recs = XFS_DFORK_DSIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
382 switch (dip->di_format) {
383 case XFS_DINODE_FMT_EXTENTS:
384 if (nextents > fork_recs)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700385 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700386 break;
387 case XFS_DINODE_FMT_BTREE:
388 if (nextents <= fork_recs)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700389 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700390 break;
391 default:
392 if (nextents != 0)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700393 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700394 break;
395 }
396
397 /* di_forkoff */
398 if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700399 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700400 if (dip->di_anextents != 0 && dip->di_forkoff == 0)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700401 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700402 if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700403 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700404
405 /* di_aformat */
406 if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
407 dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
408 dip->di_aformat != XFS_DINODE_FMT_BTREE)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700409 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700410
411 /* di_anextents */
412 nextents = be16_to_cpu(dip->di_anextents);
413 fork_recs = XFS_DFORK_ASIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
414 switch (dip->di_aformat) {
415 case XFS_DINODE_FMT_EXTENTS:
416 if (nextents > fork_recs)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700417 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700418 break;
419 case XFS_DINODE_FMT_BTREE:
420 if (nextents <= fork_recs)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700421 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700422 break;
423 default:
424 if (nextents != 0)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700425 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700426 }
427
428 if (dip->di_version >= 3) {
Darrick J. Wongf93e54362020-08-17 09:59:07 -0700429 xchk_dinode_nsec(sc, ino, dip, dip->di_crtime);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700430 xchk_inode_flags2(sc, dip, ino, mode, flags, flags2);
431 xchk_inode_cowextsize(sc, dip, ino, mode, flags,
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700432 flags2);
433 }
434}
435
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800436/*
437 * Make sure the finobt doesn't think this inode is free.
438 * We don't have to check the inobt ourselves because we got the inode via
439 * IGET_UNTRUSTED, which checks the inobt for us.
440 */
441static void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700442xchk_inode_xref_finobt(
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700443 struct xfs_scrub *sc,
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800444 xfs_ino_t ino)
445{
446 struct xfs_inobt_rec_incore rec;
447 xfs_agino_t agino;
448 int has_record;
449 int error;
450
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700451 if (!sc->sa.fino_cur || xchk_skip_xref(sc->sm))
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800452 return;
453
454 agino = XFS_INO_TO_AGINO(sc->mp, ino);
455
456 /*
457 * Try to get the finobt record. If we can't get it, then we're
458 * in good shape.
459 */
460 error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE,
461 &has_record);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700462 if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800463 !has_record)
464 return;
465
466 error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700467 if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800468 !has_record)
469 return;
470
471 /*
472 * Otherwise, make sure this record either doesn't cover this inode,
473 * or that it does but it's marked present.
474 */
475 if (rec.ir_startino > agino ||
476 rec.ir_startino + XFS_INODES_PER_CHUNK <= agino)
477 return;
478
479 if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700480 xchk_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800481}
482
Darrick J. Wong561f6482018-01-16 18:53:10 -0800483/* Cross reference the inode fields with the forks. */
484STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700485xchk_inode_xref_bmap(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700486 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700487 struct xfs_dinode *dip)
Darrick J. Wong561f6482018-01-16 18:53:10 -0800488{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700489 xfs_extnum_t nextents;
490 xfs_filblks_t count;
491 xfs_filblks_t acount;
492 int error;
Darrick J. Wong561f6482018-01-16 18:53:10 -0800493
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700494 if (xchk_skip_xref(sc->sm))
Darrick J. Wong8389f3ff2018-05-14 06:34:31 -0700495 return;
496
Darrick J. Wong561f6482018-01-16 18:53:10 -0800497 /* Walk all the extents to check nextents/naextents/nblocks. */
498 error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
499 &nextents, &count);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700500 if (!xchk_should_check_xref(sc, &error, NULL))
Darrick J. Wong561f6482018-01-16 18:53:10 -0800501 return;
502 if (nextents < be32_to_cpu(dip->di_nextents))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700503 xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
Darrick J. Wong561f6482018-01-16 18:53:10 -0800504
505 error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
506 &nextents, &acount);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700507 if (!xchk_should_check_xref(sc, &error, NULL))
Darrick J. Wong561f6482018-01-16 18:53:10 -0800508 return;
509 if (nextents != be16_to_cpu(dip->di_anextents))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700510 xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
Darrick J. Wong561f6482018-01-16 18:53:10 -0800511
512 /* Check nblocks against the inode. */
513 if (count + acount != be64_to_cpu(dip->di_nblocks))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700514 xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
Darrick J. Wong561f6482018-01-16 18:53:10 -0800515}
516
Darrick J. Wong166d7642018-01-16 18:53:05 -0800517/* Cross-reference with the other btrees. */
518STATIC void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700519xchk_inode_xref(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700520 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700521 xfs_ino_t ino,
522 struct xfs_dinode *dip)
Darrick J. Wong166d7642018-01-16 18:53:05 -0800523{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700524 xfs_agnumber_t agno;
525 xfs_agblock_t agbno;
526 int error;
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800527
Darrick J. Wong166d7642018-01-16 18:53:05 -0800528 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
529 return;
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800530
531 agno = XFS_INO_TO_AGNO(sc->mp, ino);
532 agbno = XFS_INO_TO_AGBNO(sc->mp, ino);
533
Darrick J. Wong48c66152021-08-06 11:06:35 -0700534 error = xchk_ag_init_existing(sc, agno, &sc->sa);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700535 if (!xchk_xref_process_error(sc, agno, agbno, &error))
Darrick J. Wong61e0d0c2021-08-19 12:07:49 -0700536 goto out_free;
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800537
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700538 xchk_xref_is_used_space(sc, agbno, 1);
539 xchk_inode_xref_finobt(sc, ino);
Darrick J. Wong7280fed2018-12-12 08:46:23 -0800540 xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700541 xchk_xref_is_not_shared(sc, agbno, 1);
542 xchk_inode_xref_bmap(sc, dip);
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800543
Darrick J. Wong61e0d0c2021-08-19 12:07:49 -0700544out_free:
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700545 xchk_ag_free(sc, &sc->sa);
Darrick J. Wong166d7642018-01-16 18:53:05 -0800546}
547
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800548/*
549 * If the reflink iflag disagrees with a scan for shared data fork extents,
550 * either flag an error (shared extents w/ no flag) or a preen (flag set w/o
551 * any shared extents). We already checked for reflink iflag set on a non
552 * reflink filesystem.
553 */
554static void
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700555xchk_inode_check_reflink_iflag(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700556 struct xfs_scrub *sc,
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700557 xfs_ino_t ino)
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800558{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700559 struct xfs_mount *mp = sc->mp;
560 bool has_shared;
561 int error;
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800562
Dave Chinner38c26bf2021-08-18 18:46:37 -0700563 if (!xfs_has_reflink(mp))
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800564 return;
565
566 error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
567 &has_shared);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700568 if (!xchk_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800569 XFS_INO_TO_AGBNO(mp, ino), &error))
570 return;
571 if (xfs_is_reflink_inode(sc->ip) && !has_shared)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700572 xchk_ino_set_preen(sc, ino);
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800573 else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700574 xchk_ino_set_corrupt(sc, ino);
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800575}
576
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700577/* Scrub an inode. */
578int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700579xchk_inode(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700580 struct xfs_scrub *sc)
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700581{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700582 struct xfs_dinode di;
583 int error = 0;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700584
Darrick J. Wongd0018ad2018-03-23 10:06:54 -0700585 /*
586 * If sc->ip is NULL, that means that the setup function called
587 * xfs_iget to look up the inode. xfs_iget returned a EFSCORRUPTED
588 * and a NULL inode, so flag the corruption error and return.
589 */
590 if (!sc->ip) {
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700591 xchk_ino_set_corrupt(sc, sc->sm->sm_ino);
Darrick J. Wongd0018ad2018-03-23 10:06:54 -0700592 return 0;
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700593 }
594
Darrick J. Wongd0018ad2018-03-23 10:06:54 -0700595 /* Scrub the inode core. */
596 xfs_inode_to_disk(sc->ip, &di, 0);
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700597 xchk_dinode(sc, &di, sc->ip->i_ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700598 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
599 goto out;
600
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700601 /*
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800602 * Look for discrepancies between file's data blocks and the reflink
603 * iflag. We already checked the iflag against the file mode when
604 * we scrubbed the dinode.
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700605 */
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800606 if (S_ISREG(VFS_I(sc->ip)->i_mode))
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700607 xchk_inode_check_reflink_iflag(sc, sc->ip->i_ino);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700608
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700609 xchk_inode_xref(sc, sc->ip->i_ino, &di);
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700610out:
Darrick J. Wong80e4e122017-10-17 21:37:42 -0700611 return error;
612}