1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2017 Oracle. All Rights Reserved.
4*4882a593Smuzhiyun * Author: Darrick J. Wong <darrick.wong@oracle.com>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun #include "xfs.h"
7*4882a593Smuzhiyun #include "xfs_fs.h"
8*4882a593Smuzhiyun #include "xfs_shared.h"
9*4882a593Smuzhiyun #include "xfs_format.h"
10*4882a593Smuzhiyun #include "xfs_trans_resv.h"
11*4882a593Smuzhiyun #include "xfs_mount.h"
12*4882a593Smuzhiyun #include "xfs_btree.h"
13*4882a593Smuzhiyun #include "xfs_log_format.h"
14*4882a593Smuzhiyun #include "xfs_inode.h"
15*4882a593Smuzhiyun #include "xfs_ialloc.h"
16*4882a593Smuzhiyun #include "xfs_da_format.h"
17*4882a593Smuzhiyun #include "xfs_reflink.h"
18*4882a593Smuzhiyun #include "xfs_rmap.h"
19*4882a593Smuzhiyun #include "xfs_bmap_util.h"
20*4882a593Smuzhiyun #include "scrub/scrub.h"
21*4882a593Smuzhiyun #include "scrub/common.h"
22*4882a593Smuzhiyun #include "scrub/btree.h"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /*
25*4882a593Smuzhiyun * Grab total control of the inode metadata. It doesn't matter here if
26*4882a593Smuzhiyun * the file data is still changing; exclusive access to the metadata is
27*4882a593Smuzhiyun * the goal.
28*4882a593Smuzhiyun */
29*4882a593Smuzhiyun int
xchk_setup_inode(struct xfs_scrub * sc,struct xfs_inode * ip)30*4882a593Smuzhiyun xchk_setup_inode(
31*4882a593Smuzhiyun struct xfs_scrub *sc,
32*4882a593Smuzhiyun struct xfs_inode *ip)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun int error;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /*
37*4882a593Smuzhiyun * Try to get the inode. If the verifiers fail, we try again
38*4882a593Smuzhiyun * in raw mode.
39*4882a593Smuzhiyun */
40*4882a593Smuzhiyun error = xchk_get_inode(sc, ip);
41*4882a593Smuzhiyun switch (error) {
42*4882a593Smuzhiyun case 0:
43*4882a593Smuzhiyun break;
44*4882a593Smuzhiyun case -EFSCORRUPTED:
45*4882a593Smuzhiyun case -EFSBADCRC:
46*4882a593Smuzhiyun return xchk_trans_alloc(sc, 0);
47*4882a593Smuzhiyun default:
48*4882a593Smuzhiyun return error;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* Got the inode, lock it and we're ready to go. */
52*4882a593Smuzhiyun sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
53*4882a593Smuzhiyun xfs_ilock(sc->ip, sc->ilock_flags);
54*4882a593Smuzhiyun error = xchk_trans_alloc(sc, 0);
55*4882a593Smuzhiyun if (error)
56*4882a593Smuzhiyun goto out;
57*4882a593Smuzhiyun sc->ilock_flags |= XFS_ILOCK_EXCL;
58*4882a593Smuzhiyun xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun out:
61*4882a593Smuzhiyun /* scrub teardown will unlock and release the inode for us */
62*4882a593Smuzhiyun return error;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun /* Inode core */
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /* Validate di_extsize hint. */
68*4882a593Smuzhiyun STATIC void
xchk_inode_extsize(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags)69*4882a593Smuzhiyun xchk_inode_extsize(
70*4882a593Smuzhiyun struct xfs_scrub *sc,
71*4882a593Smuzhiyun struct xfs_dinode *dip,
72*4882a593Smuzhiyun xfs_ino_t ino,
73*4882a593Smuzhiyun uint16_t mode,
74*4882a593Smuzhiyun uint16_t flags)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun xfs_failaddr_t fa;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun fa = xfs_inode_validate_extsize(sc->mp, be32_to_cpu(dip->di_extsize),
79*4882a593Smuzhiyun mode, flags);
80*4882a593Smuzhiyun if (fa)
81*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /*
85*4882a593Smuzhiyun * Validate di_cowextsize hint.
86*4882a593Smuzhiyun *
87*4882a593Smuzhiyun * The rules are documented at xfs_ioctl_setattr_check_cowextsize().
88*4882a593Smuzhiyun * These functions must be kept in sync with each other.
89*4882a593Smuzhiyun */
90*4882a593Smuzhiyun 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)91*4882a593Smuzhiyun xchk_inode_cowextsize(
92*4882a593Smuzhiyun struct xfs_scrub *sc,
93*4882a593Smuzhiyun struct xfs_dinode *dip,
94*4882a593Smuzhiyun xfs_ino_t ino,
95*4882a593Smuzhiyun uint16_t mode,
96*4882a593Smuzhiyun uint16_t flags,
97*4882a593Smuzhiyun uint64_t flags2)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun xfs_failaddr_t fa;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun fa = xfs_inode_validate_cowextsize(sc->mp,
102*4882a593Smuzhiyun be32_to_cpu(dip->di_cowextsize), mode, flags,
103*4882a593Smuzhiyun flags2);
104*4882a593Smuzhiyun if (fa)
105*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /* Make sure the di_flags make sense for the inode. */
109*4882a593Smuzhiyun STATIC void
xchk_inode_flags(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino,uint16_t mode,uint16_t flags)110*4882a593Smuzhiyun xchk_inode_flags(
111*4882a593Smuzhiyun struct xfs_scrub *sc,
112*4882a593Smuzhiyun struct xfs_dinode *dip,
113*4882a593Smuzhiyun xfs_ino_t ino,
114*4882a593Smuzhiyun uint16_t mode,
115*4882a593Smuzhiyun uint16_t flags)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun struct xfs_mount *mp = sc->mp;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /* di_flags are all taken, last bit cannot be used */
120*4882a593Smuzhiyun if (flags & ~XFS_DIFLAG_ANY)
121*4882a593Smuzhiyun goto bad;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* rt flags require rt device */
124*4882a593Smuzhiyun if ((flags & XFS_DIFLAG_REALTIME) && !mp->m_rtdev_targp)
125*4882a593Smuzhiyun goto bad;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* new rt bitmap flag only valid for rbmino */
128*4882a593Smuzhiyun if ((flags & XFS_DIFLAG_NEWRTBM) && ino != mp->m_sb.sb_rbmino)
129*4882a593Smuzhiyun goto bad;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /* directory-only flags */
132*4882a593Smuzhiyun if ((flags & (XFS_DIFLAG_RTINHERIT |
133*4882a593Smuzhiyun XFS_DIFLAG_EXTSZINHERIT |
134*4882a593Smuzhiyun XFS_DIFLAG_PROJINHERIT |
135*4882a593Smuzhiyun XFS_DIFLAG_NOSYMLINKS)) &&
136*4882a593Smuzhiyun !S_ISDIR(mode))
137*4882a593Smuzhiyun goto bad;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun /* file-only flags */
140*4882a593Smuzhiyun if ((flags & (XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE)) &&
141*4882a593Smuzhiyun !S_ISREG(mode))
142*4882a593Smuzhiyun goto bad;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* filestreams and rt make no sense */
145*4882a593Smuzhiyun if ((flags & XFS_DIFLAG_FILESTREAM) && (flags & XFS_DIFLAG_REALTIME))
146*4882a593Smuzhiyun goto bad;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return;
149*4882a593Smuzhiyun bad:
150*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun /* Make sure the di_flags2 make sense for the inode. */
154*4882a593Smuzhiyun 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)155*4882a593Smuzhiyun xchk_inode_flags2(
156*4882a593Smuzhiyun struct xfs_scrub *sc,
157*4882a593Smuzhiyun struct xfs_dinode *dip,
158*4882a593Smuzhiyun xfs_ino_t ino,
159*4882a593Smuzhiyun uint16_t mode,
160*4882a593Smuzhiyun uint16_t flags,
161*4882a593Smuzhiyun uint64_t flags2)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun struct xfs_mount *mp = sc->mp;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /* Unknown di_flags2 could be from a future kernel */
166*4882a593Smuzhiyun if (flags2 & ~XFS_DIFLAG2_ANY)
167*4882a593Smuzhiyun xchk_ino_set_warning(sc, ino);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun /* reflink flag requires reflink feature */
170*4882a593Smuzhiyun if ((flags2 & XFS_DIFLAG2_REFLINK) &&
171*4882a593Smuzhiyun !xfs_sb_version_hasreflink(&mp->m_sb))
172*4882a593Smuzhiyun goto bad;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun /* cowextsize flag is checked w.r.t. mode separately */
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* file/dir-only flags */
177*4882a593Smuzhiyun if ((flags2 & XFS_DIFLAG2_DAX) && !(S_ISREG(mode) || S_ISDIR(mode)))
178*4882a593Smuzhiyun goto bad;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun /* file-only flags */
181*4882a593Smuzhiyun if ((flags2 & XFS_DIFLAG2_REFLINK) && !S_ISREG(mode))
182*4882a593Smuzhiyun goto bad;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun /* realtime and reflink make no sense, currently */
185*4882a593Smuzhiyun if ((flags & XFS_DIFLAG_REALTIME) && (flags2 & XFS_DIFLAG2_REFLINK))
186*4882a593Smuzhiyun goto bad;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun /* dax and reflink make no sense, currently */
189*4882a593Smuzhiyun if ((flags2 & XFS_DIFLAG2_DAX) && (flags2 & XFS_DIFLAG2_REFLINK))
190*4882a593Smuzhiyun goto bad;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* no bigtime iflag without the bigtime feature */
193*4882a593Smuzhiyun if (xfs_dinode_has_bigtime(dip) &&
194*4882a593Smuzhiyun !xfs_sb_version_hasbigtime(&mp->m_sb))
195*4882a593Smuzhiyun goto bad;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun return;
198*4882a593Smuzhiyun bad:
199*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun static inline void
xchk_dinode_nsec(struct xfs_scrub * sc,xfs_ino_t ino,struct xfs_dinode * dip,const xfs_timestamp_t ts)203*4882a593Smuzhiyun xchk_dinode_nsec(
204*4882a593Smuzhiyun struct xfs_scrub *sc,
205*4882a593Smuzhiyun xfs_ino_t ino,
206*4882a593Smuzhiyun struct xfs_dinode *dip,
207*4882a593Smuzhiyun const xfs_timestamp_t ts)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun struct timespec64 tv;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun tv = xfs_inode_from_disk_ts(dip, ts);
212*4882a593Smuzhiyun if (tv.tv_nsec < 0 || tv.tv_nsec >= NSEC_PER_SEC)
213*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun /* Scrub all the ondisk inode fields. */
217*4882a593Smuzhiyun STATIC void
xchk_dinode(struct xfs_scrub * sc,struct xfs_dinode * dip,xfs_ino_t ino)218*4882a593Smuzhiyun xchk_dinode(
219*4882a593Smuzhiyun struct xfs_scrub *sc,
220*4882a593Smuzhiyun struct xfs_dinode *dip,
221*4882a593Smuzhiyun xfs_ino_t ino)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun struct xfs_mount *mp = sc->mp;
224*4882a593Smuzhiyun size_t fork_recs;
225*4882a593Smuzhiyun unsigned long long isize;
226*4882a593Smuzhiyun uint64_t flags2;
227*4882a593Smuzhiyun uint32_t nextents;
228*4882a593Smuzhiyun uint16_t flags;
229*4882a593Smuzhiyun uint16_t mode;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun flags = be16_to_cpu(dip->di_flags);
232*4882a593Smuzhiyun if (dip->di_version >= 3)
233*4882a593Smuzhiyun flags2 = be64_to_cpu(dip->di_flags2);
234*4882a593Smuzhiyun else
235*4882a593Smuzhiyun flags2 = 0;
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* di_mode */
238*4882a593Smuzhiyun mode = be16_to_cpu(dip->di_mode);
239*4882a593Smuzhiyun switch (mode & S_IFMT) {
240*4882a593Smuzhiyun case S_IFLNK:
241*4882a593Smuzhiyun case S_IFREG:
242*4882a593Smuzhiyun case S_IFDIR:
243*4882a593Smuzhiyun case S_IFCHR:
244*4882a593Smuzhiyun case S_IFBLK:
245*4882a593Smuzhiyun case S_IFIFO:
246*4882a593Smuzhiyun case S_IFSOCK:
247*4882a593Smuzhiyun /* mode is recognized */
248*4882a593Smuzhiyun break;
249*4882a593Smuzhiyun default:
250*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
251*4882a593Smuzhiyun break;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* v1/v2 fields */
255*4882a593Smuzhiyun switch (dip->di_version) {
256*4882a593Smuzhiyun case 1:
257*4882a593Smuzhiyun /*
258*4882a593Smuzhiyun * We autoconvert v1 inodes into v2 inodes on writeout,
259*4882a593Smuzhiyun * so just mark this inode for preening.
260*4882a593Smuzhiyun */
261*4882a593Smuzhiyun xchk_ino_set_preen(sc, ino);
262*4882a593Smuzhiyun break;
263*4882a593Smuzhiyun case 2:
264*4882a593Smuzhiyun case 3:
265*4882a593Smuzhiyun if (dip->di_onlink != 0)
266*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun if (dip->di_mode == 0 && sc->ip)
269*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun if (dip->di_projid_hi != 0 &&
272*4882a593Smuzhiyun !xfs_sb_version_hasprojid32bit(&mp->m_sb))
273*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
274*4882a593Smuzhiyun break;
275*4882a593Smuzhiyun default:
276*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
277*4882a593Smuzhiyun return;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /*
281*4882a593Smuzhiyun * di_uid/di_gid -- -1 isn't invalid, but there's no way that
282*4882a593Smuzhiyun * userspace could have created that.
283*4882a593Smuzhiyun */
284*4882a593Smuzhiyun if (dip->di_uid == cpu_to_be32(-1U) ||
285*4882a593Smuzhiyun dip->di_gid == cpu_to_be32(-1U))
286*4882a593Smuzhiyun xchk_ino_set_warning(sc, ino);
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun /* di_format */
289*4882a593Smuzhiyun switch (dip->di_format) {
290*4882a593Smuzhiyun case XFS_DINODE_FMT_DEV:
291*4882a593Smuzhiyun if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
292*4882a593Smuzhiyun !S_ISFIFO(mode) && !S_ISSOCK(mode))
293*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
294*4882a593Smuzhiyun break;
295*4882a593Smuzhiyun case XFS_DINODE_FMT_LOCAL:
296*4882a593Smuzhiyun if (!S_ISDIR(mode) && !S_ISLNK(mode))
297*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
298*4882a593Smuzhiyun break;
299*4882a593Smuzhiyun case XFS_DINODE_FMT_EXTENTS:
300*4882a593Smuzhiyun if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
301*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
302*4882a593Smuzhiyun break;
303*4882a593Smuzhiyun case XFS_DINODE_FMT_BTREE:
304*4882a593Smuzhiyun if (!S_ISREG(mode) && !S_ISDIR(mode))
305*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
306*4882a593Smuzhiyun break;
307*4882a593Smuzhiyun case XFS_DINODE_FMT_UUID:
308*4882a593Smuzhiyun default:
309*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
310*4882a593Smuzhiyun break;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun /* di_[amc]time.nsec */
314*4882a593Smuzhiyun xchk_dinode_nsec(sc, ino, dip, dip->di_atime);
315*4882a593Smuzhiyun xchk_dinode_nsec(sc, ino, dip, dip->di_mtime);
316*4882a593Smuzhiyun xchk_dinode_nsec(sc, ino, dip, dip->di_ctime);
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun /*
319*4882a593Smuzhiyun * di_size. xfs_dinode_verify checks for things that screw up
320*4882a593Smuzhiyun * the VFS such as the upper bit being set and zero-length
321*4882a593Smuzhiyun * symlinks/directories, but we can do more here.
322*4882a593Smuzhiyun */
323*4882a593Smuzhiyun isize = be64_to_cpu(dip->di_size);
324*4882a593Smuzhiyun if (isize & (1ULL << 63))
325*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun /* Devices, fifos, and sockets must have zero size */
328*4882a593Smuzhiyun if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
329*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun /* Directories can't be larger than the data section size (32G) */
332*4882a593Smuzhiyun if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
333*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun /* Symlinks can't be larger than SYMLINK_MAXLEN */
336*4882a593Smuzhiyun if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
337*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun /*
340*4882a593Smuzhiyun * Warn if the running kernel can't handle the kinds of offsets
341*4882a593Smuzhiyun * needed to deal with the file size. In other words, if the
342*4882a593Smuzhiyun * pagecache can't cache all the blocks in this file due to
343*4882a593Smuzhiyun * overly large offsets, flag the inode for admin review.
344*4882a593Smuzhiyun */
345*4882a593Smuzhiyun if (isize >= mp->m_super->s_maxbytes)
346*4882a593Smuzhiyun xchk_ino_set_warning(sc, ino);
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun /* di_nblocks */
349*4882a593Smuzhiyun if (flags2 & XFS_DIFLAG2_REFLINK) {
350*4882a593Smuzhiyun ; /* nblocks can exceed dblocks */
351*4882a593Smuzhiyun } else if (flags & XFS_DIFLAG_REALTIME) {
352*4882a593Smuzhiyun /*
353*4882a593Smuzhiyun * nblocks is the sum of data extents (in the rtdev),
354*4882a593Smuzhiyun * attr extents (in the datadev), and both forks' bmbt
355*4882a593Smuzhiyun * blocks (in the datadev). This clumsy check is the
356*4882a593Smuzhiyun * best we can do without cross-referencing with the
357*4882a593Smuzhiyun * inode forks.
358*4882a593Smuzhiyun */
359*4882a593Smuzhiyun if (be64_to_cpu(dip->di_nblocks) >=
360*4882a593Smuzhiyun mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
361*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
362*4882a593Smuzhiyun } else {
363*4882a593Smuzhiyun if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
364*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun xchk_inode_flags(sc, dip, ino, mode, flags);
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun xchk_inode_extsize(sc, dip, ino, mode, flags);
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* di_nextents */
372*4882a593Smuzhiyun nextents = be32_to_cpu(dip->di_nextents);
373*4882a593Smuzhiyun fork_recs = XFS_DFORK_DSIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
374*4882a593Smuzhiyun switch (dip->di_format) {
375*4882a593Smuzhiyun case XFS_DINODE_FMT_EXTENTS:
376*4882a593Smuzhiyun if (nextents > fork_recs)
377*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
378*4882a593Smuzhiyun break;
379*4882a593Smuzhiyun case XFS_DINODE_FMT_BTREE:
380*4882a593Smuzhiyun if (nextents <= fork_recs)
381*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
382*4882a593Smuzhiyun break;
383*4882a593Smuzhiyun default:
384*4882a593Smuzhiyun if (nextents != 0)
385*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
386*4882a593Smuzhiyun break;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun /* di_forkoff */
390*4882a593Smuzhiyun if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
391*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
392*4882a593Smuzhiyun if (dip->di_anextents != 0 && dip->di_forkoff == 0)
393*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
394*4882a593Smuzhiyun if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
395*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun /* di_aformat */
398*4882a593Smuzhiyun if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
399*4882a593Smuzhiyun dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
400*4882a593Smuzhiyun dip->di_aformat != XFS_DINODE_FMT_BTREE)
401*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun /* di_anextents */
404*4882a593Smuzhiyun nextents = be16_to_cpu(dip->di_anextents);
405*4882a593Smuzhiyun fork_recs = XFS_DFORK_ASIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
406*4882a593Smuzhiyun switch (dip->di_aformat) {
407*4882a593Smuzhiyun case XFS_DINODE_FMT_EXTENTS:
408*4882a593Smuzhiyun if (nextents > fork_recs)
409*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
410*4882a593Smuzhiyun break;
411*4882a593Smuzhiyun case XFS_DINODE_FMT_BTREE:
412*4882a593Smuzhiyun if (nextents <= fork_recs)
413*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
414*4882a593Smuzhiyun break;
415*4882a593Smuzhiyun default:
416*4882a593Smuzhiyun if (nextents != 0)
417*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun if (dip->di_version >= 3) {
421*4882a593Smuzhiyun xchk_dinode_nsec(sc, ino, dip, dip->di_crtime);
422*4882a593Smuzhiyun xchk_inode_flags2(sc, dip, ino, mode, flags, flags2);
423*4882a593Smuzhiyun xchk_inode_cowextsize(sc, dip, ino, mode, flags,
424*4882a593Smuzhiyun flags2);
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun /*
429*4882a593Smuzhiyun * Make sure the finobt doesn't think this inode is free.
430*4882a593Smuzhiyun * We don't have to check the inobt ourselves because we got the inode via
431*4882a593Smuzhiyun * IGET_UNTRUSTED, which checks the inobt for us.
432*4882a593Smuzhiyun */
433*4882a593Smuzhiyun static void
xchk_inode_xref_finobt(struct xfs_scrub * sc,xfs_ino_t ino)434*4882a593Smuzhiyun xchk_inode_xref_finobt(
435*4882a593Smuzhiyun struct xfs_scrub *sc,
436*4882a593Smuzhiyun xfs_ino_t ino)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun struct xfs_inobt_rec_incore rec;
439*4882a593Smuzhiyun xfs_agino_t agino;
440*4882a593Smuzhiyun int has_record;
441*4882a593Smuzhiyun int error;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun if (!sc->sa.fino_cur || xchk_skip_xref(sc->sm))
444*4882a593Smuzhiyun return;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun agino = XFS_INO_TO_AGINO(sc->mp, ino);
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun /*
449*4882a593Smuzhiyun * Try to get the finobt record. If we can't get it, then we're
450*4882a593Smuzhiyun * in good shape.
451*4882a593Smuzhiyun */
452*4882a593Smuzhiyun error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE,
453*4882a593Smuzhiyun &has_record);
454*4882a593Smuzhiyun if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
455*4882a593Smuzhiyun !has_record)
456*4882a593Smuzhiyun return;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record);
459*4882a593Smuzhiyun if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
460*4882a593Smuzhiyun !has_record)
461*4882a593Smuzhiyun return;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun /*
464*4882a593Smuzhiyun * Otherwise, make sure this record either doesn't cover this inode,
465*4882a593Smuzhiyun * or that it does but it's marked present.
466*4882a593Smuzhiyun */
467*4882a593Smuzhiyun if (rec.ir_startino > agino ||
468*4882a593Smuzhiyun rec.ir_startino + XFS_INODES_PER_CHUNK <= agino)
469*4882a593Smuzhiyun return;
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))
472*4882a593Smuzhiyun xchk_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun /* Cross reference the inode fields with the forks. */
476*4882a593Smuzhiyun STATIC void
xchk_inode_xref_bmap(struct xfs_scrub * sc,struct xfs_dinode * dip)477*4882a593Smuzhiyun xchk_inode_xref_bmap(
478*4882a593Smuzhiyun struct xfs_scrub *sc,
479*4882a593Smuzhiyun struct xfs_dinode *dip)
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun xfs_extnum_t nextents;
482*4882a593Smuzhiyun xfs_filblks_t count;
483*4882a593Smuzhiyun xfs_filblks_t acount;
484*4882a593Smuzhiyun int error;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun if (xchk_skip_xref(sc->sm))
487*4882a593Smuzhiyun return;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun /* Walk all the extents to check nextents/naextents/nblocks. */
490*4882a593Smuzhiyun error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
491*4882a593Smuzhiyun &nextents, &count);
492*4882a593Smuzhiyun if (!xchk_should_check_xref(sc, &error, NULL))
493*4882a593Smuzhiyun return;
494*4882a593Smuzhiyun if (nextents < be32_to_cpu(dip->di_nextents))
495*4882a593Smuzhiyun xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
498*4882a593Smuzhiyun &nextents, &acount);
499*4882a593Smuzhiyun if (!xchk_should_check_xref(sc, &error, NULL))
500*4882a593Smuzhiyun return;
501*4882a593Smuzhiyun if (nextents != be16_to_cpu(dip->di_anextents))
502*4882a593Smuzhiyun xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun /* Check nblocks against the inode. */
505*4882a593Smuzhiyun if (count + acount != be64_to_cpu(dip->di_nblocks))
506*4882a593Smuzhiyun xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun /* Cross-reference with the other btrees. */
510*4882a593Smuzhiyun STATIC void
xchk_inode_xref(struct xfs_scrub * sc,xfs_ino_t ino,struct xfs_dinode * dip)511*4882a593Smuzhiyun xchk_inode_xref(
512*4882a593Smuzhiyun struct xfs_scrub *sc,
513*4882a593Smuzhiyun xfs_ino_t ino,
514*4882a593Smuzhiyun struct xfs_dinode *dip)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun xfs_agnumber_t agno;
517*4882a593Smuzhiyun xfs_agblock_t agbno;
518*4882a593Smuzhiyun int error;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
521*4882a593Smuzhiyun return;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun agno = XFS_INO_TO_AGNO(sc->mp, ino);
524*4882a593Smuzhiyun agbno = XFS_INO_TO_AGBNO(sc->mp, ino);
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun error = xchk_ag_init(sc, agno, &sc->sa);
527*4882a593Smuzhiyun if (!xchk_xref_process_error(sc, agno, agbno, &error))
528*4882a593Smuzhiyun return;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun xchk_xref_is_used_space(sc, agbno, 1);
531*4882a593Smuzhiyun xchk_inode_xref_finobt(sc, ino);
532*4882a593Smuzhiyun xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
533*4882a593Smuzhiyun xchk_xref_is_not_shared(sc, agbno, 1);
534*4882a593Smuzhiyun xchk_inode_xref_bmap(sc, dip);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun xchk_ag_free(sc, &sc->sa);
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun /*
540*4882a593Smuzhiyun * If the reflink iflag disagrees with a scan for shared data fork extents,
541*4882a593Smuzhiyun * either flag an error (shared extents w/ no flag) or a preen (flag set w/o
542*4882a593Smuzhiyun * any shared extents). We already checked for reflink iflag set on a non
543*4882a593Smuzhiyun * reflink filesystem.
544*4882a593Smuzhiyun */
545*4882a593Smuzhiyun static void
xchk_inode_check_reflink_iflag(struct xfs_scrub * sc,xfs_ino_t ino)546*4882a593Smuzhiyun xchk_inode_check_reflink_iflag(
547*4882a593Smuzhiyun struct xfs_scrub *sc,
548*4882a593Smuzhiyun xfs_ino_t ino)
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun struct xfs_mount *mp = sc->mp;
551*4882a593Smuzhiyun bool has_shared;
552*4882a593Smuzhiyun int error;
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun if (!xfs_sb_version_hasreflink(&mp->m_sb))
555*4882a593Smuzhiyun return;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
558*4882a593Smuzhiyun &has_shared);
559*4882a593Smuzhiyun if (!xchk_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
560*4882a593Smuzhiyun XFS_INO_TO_AGBNO(mp, ino), &error))
561*4882a593Smuzhiyun return;
562*4882a593Smuzhiyun if (xfs_is_reflink_inode(sc->ip) && !has_shared)
563*4882a593Smuzhiyun xchk_ino_set_preen(sc, ino);
564*4882a593Smuzhiyun else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
565*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, ino);
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun /* Scrub an inode. */
569*4882a593Smuzhiyun int
xchk_inode(struct xfs_scrub * sc)570*4882a593Smuzhiyun xchk_inode(
571*4882a593Smuzhiyun struct xfs_scrub *sc)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun struct xfs_dinode di;
574*4882a593Smuzhiyun int error = 0;
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun /*
577*4882a593Smuzhiyun * If sc->ip is NULL, that means that the setup function called
578*4882a593Smuzhiyun * xfs_iget to look up the inode. xfs_iget returned a EFSCORRUPTED
579*4882a593Smuzhiyun * and a NULL inode, so flag the corruption error and return.
580*4882a593Smuzhiyun */
581*4882a593Smuzhiyun if (!sc->ip) {
582*4882a593Smuzhiyun xchk_ino_set_corrupt(sc, sc->sm->sm_ino);
583*4882a593Smuzhiyun return 0;
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun /* Scrub the inode core. */
587*4882a593Smuzhiyun xfs_inode_to_disk(sc->ip, &di, 0);
588*4882a593Smuzhiyun xchk_dinode(sc, &di, sc->ip->i_ino);
589*4882a593Smuzhiyun if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
590*4882a593Smuzhiyun goto out;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun /*
593*4882a593Smuzhiyun * Look for discrepancies between file's data blocks and the reflink
594*4882a593Smuzhiyun * iflag. We already checked the iflag against the file mode when
595*4882a593Smuzhiyun * we scrubbed the dinode.
596*4882a593Smuzhiyun */
597*4882a593Smuzhiyun if (S_ISREG(VFS_I(sc->ip)->i_mode))
598*4882a593Smuzhiyun xchk_inode_check_reflink_iflag(sc, sc->ip->i_ino);
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun xchk_inode_xref(sc, sc->ip->i_ino, &di);
601*4882a593Smuzhiyun out:
602*4882a593Smuzhiyun return error;
603*4882a593Smuzhiyun }
604