@@ -51,32 +51,201 @@ struct xchk_iallocbt {
5151};
5252
5353/*
54- * If we're checking the finobt, cross-reference with the inobt.
55- * Otherwise we're checking the inobt; if there is an finobt, make sure
56- * we have a record or not depending on freecount.
54+ * Does the finobt have a record for this inode with the same hole/free state?
55+ * This is a bit complicated because of the following:
56+ *
57+ * - The finobt need not have a record if all inodes in the inobt record are
58+ * allocated.
59+ * - The finobt need not have a record if all inodes in the inobt record are
60+ * free.
61+ * - The finobt need not have a record if the inobt record says this is a hole.
62+ * This likely doesn't happen in practice.
5763 */
58- static inline void
59- xchk_iallocbt_chunk_xref_other (
64+ STATIC int
65+ xchk_inobt_xref_finobt (
66+ struct xfs_scrub * sc ,
67+ struct xfs_inobt_rec_incore * irec ,
68+ xfs_agino_t agino ,
69+ bool free ,
70+ bool hole )
71+ {
72+ struct xfs_inobt_rec_incore frec ;
73+ struct xfs_btree_cur * cur = sc -> sa .fino_cur ;
74+ bool ffree , fhole ;
75+ unsigned int frec_idx , fhole_idx ;
76+ int has_record ;
77+ int error ;
78+
79+ ASSERT (cur -> bc_btnum == XFS_BTNUM_FINO );
80+
81+ error = xfs_inobt_lookup (cur , agino , XFS_LOOKUP_LE , & has_record );
82+ if (error )
83+ return error ;
84+ if (!has_record )
85+ goto no_record ;
86+
87+ error = xfs_inobt_get_rec (cur , & frec , & has_record );
88+ if (!has_record )
89+ return - EFSCORRUPTED ;
90+
91+ if (frec .ir_startino + XFS_INODES_PER_CHUNK <= agino )
92+ goto no_record ;
93+
94+ /* There's a finobt record; free and hole status must match. */
95+ frec_idx = agino - frec .ir_startino ;
96+ ffree = frec .ir_free & (1ULL << frec_idx );
97+ fhole_idx = frec_idx / XFS_INODES_PER_HOLEMASK_BIT ;
98+ fhole = frec .ir_holemask & (1U << fhole_idx );
99+
100+ if (ffree != free )
101+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
102+ if (fhole != hole )
103+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
104+ return 0 ;
105+
106+ no_record :
107+ /* inobt record is fully allocated */
108+ if (irec -> ir_free == 0 )
109+ return 0 ;
110+
111+ /* inobt record is totally unallocated */
112+ if (irec -> ir_free == XFS_INOBT_ALL_FREE )
113+ return 0 ;
114+
115+ /* inobt record says this is a hole */
116+ if (hole )
117+ return 0 ;
118+
119+ /* finobt doesn't care about allocated inodes */
120+ if (!free )
121+ return 0 ;
122+
123+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
124+ return 0 ;
125+ }
126+
127+ /*
128+ * Make sure that each inode of this part of an inobt record has the same
129+ * sparse and free status as the finobt.
130+ */
131+ STATIC void
132+ xchk_inobt_chunk_xref_finobt (
60133 struct xfs_scrub * sc ,
61134 struct xfs_inobt_rec_incore * irec ,
62- xfs_agino_t agino )
135+ xfs_agino_t agino ,
136+ unsigned int nr_inodes )
63137{
64- struct xfs_btree_cur * * pcur ;
65- bool has_irec ;
138+ xfs_agino_t i ;
139+ unsigned int rec_idx ;
66140 int error ;
67141
68- if (sc -> sm -> sm_type == XFS_SCRUB_TYPE_FINOBT )
69- pcur = & sc -> sa .ino_cur ;
70- else
71- pcur = & sc -> sa .fino_cur ;
72- if (!(* pcur ))
142+ ASSERT (sc -> sm -> sm_type == XFS_SCRUB_TYPE_INOBT );
143+
144+ if (!sc -> sa .fino_cur || xchk_skip_xref (sc -> sm ))
73145 return ;
74- error = xfs_ialloc_has_inode_record (* pcur , agino , agino , & has_irec );
75- if (!xchk_should_check_xref (sc , & error , pcur ))
146+
147+ for (i = agino , rec_idx = agino - irec -> ir_startino ;
148+ i < agino + nr_inodes ;
149+ i ++ , rec_idx ++ ) {
150+ bool free , hole ;
151+ unsigned int hole_idx ;
152+
153+ free = irec -> ir_free & (1ULL << rec_idx );
154+ hole_idx = rec_idx / XFS_INODES_PER_HOLEMASK_BIT ;
155+ hole = irec -> ir_holemask & (1U << hole_idx );
156+
157+ error = xchk_inobt_xref_finobt (sc , irec , i , free , hole );
158+ if (!xchk_should_check_xref (sc , & error , & sc -> sa .fino_cur ))
159+ return ;
160+ }
161+ }
162+
163+ /*
164+ * Does the inobt have a record for this inode with the same hole/free state?
165+ * The inobt must always have a record if there's a finobt record.
166+ */
167+ STATIC int
168+ xchk_finobt_xref_inobt (
169+ struct xfs_scrub * sc ,
170+ struct xfs_inobt_rec_incore * frec ,
171+ xfs_agino_t agino ,
172+ bool ffree ,
173+ bool fhole )
174+ {
175+ struct xfs_inobt_rec_incore irec ;
176+ struct xfs_btree_cur * cur = sc -> sa .ino_cur ;
177+ bool free , hole ;
178+ unsigned int rec_idx , hole_idx ;
179+ int has_record ;
180+ int error ;
181+
182+ ASSERT (cur -> bc_btnum == XFS_BTNUM_INO );
183+
184+ error = xfs_inobt_lookup (cur , agino , XFS_LOOKUP_LE , & has_record );
185+ if (error )
186+ return error ;
187+ if (!has_record )
188+ goto no_record ;
189+
190+ error = xfs_inobt_get_rec (cur , & irec , & has_record );
191+ if (!has_record )
192+ return - EFSCORRUPTED ;
193+
194+ if (irec .ir_startino + XFS_INODES_PER_CHUNK <= agino )
195+ goto no_record ;
196+
197+ /* There's an inobt record; free and hole status must match. */
198+ rec_idx = agino - irec .ir_startino ;
199+ free = irec .ir_free & (1ULL << rec_idx );
200+ hole_idx = rec_idx / XFS_INODES_PER_HOLEMASK_BIT ;
201+ hole = irec .ir_holemask & (1U << hole_idx );
202+
203+ if (ffree != free )
204+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
205+ if (fhole != hole )
206+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
207+ return 0 ;
208+
209+ no_record :
210+ /* finobt should never have a record for which the inobt does not */
211+ xchk_btree_xref_set_corrupt (sc , cur , 0 );
212+ return 0 ;
213+ }
214+
215+ /*
216+ * Make sure that each inode of this part of an finobt record has the same
217+ * sparse and free status as the inobt.
218+ */
219+ STATIC void
220+ xchk_finobt_chunk_xref_inobt (
221+ struct xfs_scrub * sc ,
222+ struct xfs_inobt_rec_incore * frec ,
223+ xfs_agino_t agino ,
224+ unsigned int nr_inodes )
225+ {
226+ xfs_agino_t i ;
227+ unsigned int rec_idx ;
228+ int error ;
229+
230+ ASSERT (sc -> sm -> sm_type == XFS_SCRUB_TYPE_FINOBT );
231+
232+ if (!sc -> sa .ino_cur || xchk_skip_xref (sc -> sm ))
76233 return ;
77- if (((irec -> ir_freecount > 0 && !has_irec ) ||
78- (irec -> ir_freecount == 0 && has_irec )))
79- xchk_btree_xref_set_corrupt (sc , * pcur , 0 );
234+
235+ for (i = agino , rec_idx = agino - frec -> ir_startino ;
236+ i < agino + nr_inodes ;
237+ i ++ , rec_idx ++ ) {
238+ bool ffree , fhole ;
239+ unsigned int hole_idx ;
240+
241+ ffree = frec -> ir_free & (1ULL << rec_idx );
242+ hole_idx = rec_idx / XFS_INODES_PER_HOLEMASK_BIT ;
243+ fhole = frec -> ir_holemask & (1U << hole_idx );
244+
245+ error = xchk_finobt_xref_inobt (sc , frec , i , ffree , fhole );
246+ if (!xchk_should_check_xref (sc , & error , & sc -> sa .ino_cur ))
247+ return ;
248+ }
80249}
81250
82251/* Is this chunk worth checking and cross-referencing? */
@@ -85,14 +254,16 @@ xchk_iallocbt_chunk(
85254 struct xchk_btree * bs ,
86255 struct xfs_inobt_rec_incore * irec ,
87256 xfs_agino_t agino ,
88- xfs_extlen_t len )
257+ unsigned int nr_inodes )
89258{
90259 struct xfs_scrub * sc = bs -> sc ;
91260 struct xfs_mount * mp = bs -> cur -> bc_mp ;
92261 struct xfs_perag * pag = bs -> cur -> bc_ag .pag ;
93262 xfs_agblock_t agbno ;
263+ xfs_extlen_t len ;
94264
95265 agbno = XFS_AGINO_TO_AGBNO (mp , agino );
266+ len = XFS_B_TO_FSB (mp , nr_inodes * mp -> m_sb .sb_inodesize );
96267
97268 if (!xfs_verify_agbext (pag , agbno , len ))
98269 xchk_btree_set_corrupt (bs -> sc , bs -> cur , 0 );
@@ -101,7 +272,10 @@ xchk_iallocbt_chunk(
101272 return false;
102273
103274 xchk_xref_is_used_space (sc , agbno , len );
104- xchk_iallocbt_chunk_xref_other (sc , irec , agino );
275+ if (sc -> sm -> sm_type == XFS_SCRUB_TYPE_INOBT )
276+ xchk_inobt_chunk_xref_finobt (sc , irec , agino , nr_inodes );
277+ else
278+ xchk_finobt_chunk_xref_inobt (sc , irec , agino , nr_inodes );
105279 xchk_xref_is_owned_by (sc , agbno , len , & XFS_RMAP_OINFO_INODES );
106280 xchk_xref_is_not_shared (sc , agbno , len );
107281 xchk_xref_is_not_cow_staging (sc , agbno , len );
@@ -406,7 +580,6 @@ xchk_iallocbt_rec(
406580 struct xfs_inobt_rec_incore irec ;
407581 uint64_t holes ;
408582 xfs_agino_t agino ;
409- xfs_extlen_t len ;
410583 int holecount ;
411584 int i ;
412585 int error = 0 ;
@@ -428,21 +601,18 @@ xchk_iallocbt_rec(
428601
429602 /* Handle non-sparse inodes */
430603 if (!xfs_inobt_issparse (irec .ir_holemask )) {
431- len = XFS_B_TO_FSB (mp ,
432- XFS_INODES_PER_CHUNK * mp -> m_sb .sb_inodesize );
433604 if (irec .ir_count != XFS_INODES_PER_CHUNK )
434605 xchk_btree_set_corrupt (bs -> sc , bs -> cur , 0 );
435606
436- if (!xchk_iallocbt_chunk (bs , & irec , agino , len ))
607+ if (!xchk_iallocbt_chunk (bs , & irec , agino ,
608+ XFS_INODES_PER_CHUNK ))
437609 goto out ;
438610 goto check_clusters ;
439611 }
440612
441613 /* Check each chunk of a sparse inode cluster. */
442614 holemask = irec .ir_holemask ;
443615 holecount = 0 ;
444- len = XFS_B_TO_FSB (mp ,
445- XFS_INODES_PER_HOLEMASK_BIT * mp -> m_sb .sb_inodesize );
446616 holes = ~xfs_inobt_irec_to_allocmask (& irec );
447617 if ((holes & irec .ir_free ) != holes ||
448618 irec .ir_freecount > irec .ir_count )
@@ -451,7 +621,8 @@ xchk_iallocbt_rec(
451621 for (i = 0 ; i < XFS_INOBT_HOLEMASK_BITS ; i ++ ) {
452622 if (holemask & 1 )
453623 holecount += XFS_INODES_PER_HOLEMASK_BIT ;
454- else if (!xchk_iallocbt_chunk (bs , & irec , agino , len ))
624+ else if (!xchk_iallocbt_chunk (bs , & irec , agino ,
625+ XFS_INODES_PER_HOLEMASK_BIT ))
455626 goto out ;
456627 holemask >>= 1 ;
457628 agino += XFS_INODES_PER_HOLEMASK_BIT ;
0 commit comments