Skip to content

Commit a349e4c

Browse files
committed
Merge tag 'xfs-5.10-fixes-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
Pull xfs fixes from Darrick Wong: "The critical fixes are for a crash that someone reported in the xattr code on 32-bit arm last week; and a revert of the rmap key comparison change from last week as it was totally wrong. I need a vacation. :( Summary: - Fix various deficiencies in online fsck's metadata checking code - Fix an integer casting bug in the xattr code on 32-bit systems - Fix a hang in an inode walk when the inode index is corrupt - Fix error codes being dropped when initializing per-AG structures - Fix nowait directio writes that partially succeed but return EAGAIN - Revert last week's rmap comparison patch because it was wrong" * tag 'xfs-5.10-fixes-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: xfs: revert "xfs: fix rmap key and record comparison functions" xfs: don't allow NOWAIT DIO across extent boundaries xfs: return corresponding errcode if xfs_initialize_perag() fail xfs: ensure inobt record walks always make forward progress xfs: fix forkoff miscalculation related to XFS_LITINO(mp) xfs: directory scrub should check the null bestfree entries too xfs: strengthen rmap record flags checking xfs: fix the minrecs logic when dealing with inode root child blocks
2 parents ba91110 + eb84090 commit a349e4c

8 files changed

Lines changed: 124 additions & 41 deletions

File tree

fs/xfs/libxfs/xfs_attr_leaf.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ xfs_attr_copy_value(
515515
*========================================================================*/
516516

517517
/*
518-
* Query whether the requested number of additional bytes of extended
518+
* Query whether the total requested number of attr fork bytes of extended
519519
* attribute space will be able to fit inline.
520520
*
521521
* Returns zero if not, else the di_forkoff fork offset to be used in the
@@ -535,6 +535,12 @@ xfs_attr_shortform_bytesfit(
535535
int maxforkoff;
536536
int offset;
537537

538+
/*
539+
* Check if the new size could fit at all first:
540+
*/
541+
if (bytes > XFS_LITINO(mp))
542+
return 0;
543+
538544
/* rounded down */
539545
offset = (XFS_LITINO(mp) - bytes) >> 3;
540546

fs/xfs/libxfs/xfs_rmap_btree.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ xfs_rmapbt_key_diff(
243243
else if (y > x)
244244
return -1;
245245

246-
x = be64_to_cpu(kp->rm_offset);
247-
y = xfs_rmap_irec_offset_pack(rec);
246+
x = XFS_RMAP_OFF(be64_to_cpu(kp->rm_offset));
247+
y = rec->rm_offset;
248248
if (x > y)
249249
return 1;
250250
else if (y > x)
@@ -275,8 +275,8 @@ xfs_rmapbt_diff_two_keys(
275275
else if (y > x)
276276
return -1;
277277

278-
x = be64_to_cpu(kp1->rm_offset);
279-
y = be64_to_cpu(kp2->rm_offset);
278+
x = XFS_RMAP_OFF(be64_to_cpu(kp1->rm_offset));
279+
y = XFS_RMAP_OFF(be64_to_cpu(kp2->rm_offset));
280280
if (x > y)
281281
return 1;
282282
else if (y > x)
@@ -390,8 +390,8 @@ xfs_rmapbt_keys_inorder(
390390
return 1;
391391
else if (a > b)
392392
return 0;
393-
a = be64_to_cpu(k1->rmap.rm_offset);
394-
b = be64_to_cpu(k2->rmap.rm_offset);
393+
a = XFS_RMAP_OFF(be64_to_cpu(k1->rmap.rm_offset));
394+
b = XFS_RMAP_OFF(be64_to_cpu(k2->rmap.rm_offset));
395395
if (a <= b)
396396
return 1;
397397
return 0;
@@ -420,8 +420,8 @@ xfs_rmapbt_recs_inorder(
420420
return 1;
421421
else if (a > b)
422422
return 0;
423-
a = be64_to_cpu(r1->rmap.rm_offset);
424-
b = be64_to_cpu(r2->rmap.rm_offset);
423+
a = XFS_RMAP_OFF(be64_to_cpu(r1->rmap.rm_offset));
424+
b = XFS_RMAP_OFF(be64_to_cpu(r2->rmap.rm_offset));
425425
if (a <= b)
426426
return 1;
427427
return 0;

fs/xfs/scrub/bmap.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,13 @@ xchk_bmap_xref_rmap(
218218
* which doesn't track unwritten state.
219219
*/
220220
if (owner != XFS_RMAP_OWN_COW &&
221-
irec->br_state == XFS_EXT_UNWRITTEN &&
222-
!(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
221+
!!(irec->br_state == XFS_EXT_UNWRITTEN) !=
222+
!!(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
223223
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
224224
irec->br_startoff);
225225

226-
if (info->whichfork == XFS_ATTR_FORK &&
227-
!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
226+
if (!!(info->whichfork == XFS_ATTR_FORK) !=
227+
!!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
228228
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
229229
irec->br_startoff);
230230
if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)

fs/xfs/scrub/btree.c

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -452,32 +452,41 @@ xchk_btree_check_minrecs(
452452
int level,
453453
struct xfs_btree_block *block)
454454
{
455-
unsigned int numrecs;
456-
int ok_level;
457-
458-
numrecs = be16_to_cpu(block->bb_numrecs);
455+
struct xfs_btree_cur *cur = bs->cur;
456+
unsigned int root_level = cur->bc_nlevels - 1;
457+
unsigned int numrecs = be16_to_cpu(block->bb_numrecs);
459458

460459
/* More records than minrecs means the block is ok. */
461-
if (numrecs >= bs->cur->bc_ops->get_minrecs(bs->cur, level))
460+
if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
462461
return;
463462

464463
/*
465-
* Certain btree blocks /can/ have fewer than minrecs records. Any
466-
* level greater than or equal to the level of the highest dedicated
467-
* btree block are allowed to violate this constraint.
468-
*
469-
* For a btree rooted in a block, the btree root can have fewer than
470-
* minrecs records. If the btree is rooted in an inode and does not
471-
* store records in the root, the direct children of the root and the
472-
* root itself can have fewer than minrecs records.
464+
* For btrees rooted in the inode, it's possible that the root block
465+
* contents spilled into a regular ondisk block because there wasn't
466+
* enough space in the inode root. The number of records in that
467+
* child block might be less than the standard minrecs, but that's ok
468+
* provided that there's only one direct child of the root.
473469
*/
474-
ok_level = bs->cur->bc_nlevels - 1;
475-
if (bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
476-
ok_level--;
477-
if (level >= ok_level)
470+
if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
471+
level == cur->bc_nlevels - 2) {
472+
struct xfs_btree_block *root_block;
473+
struct xfs_buf *root_bp;
474+
int root_maxrecs;
475+
476+
root_block = xfs_btree_get_block(cur, root_level, &root_bp);
477+
root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
478+
if (be16_to_cpu(root_block->bb_numrecs) != 1 ||
479+
numrecs <= root_maxrecs)
480+
xchk_btree_set_corrupt(bs->sc, cur, level);
478481
return;
482+
}
479483

480-
xchk_btree_set_corrupt(bs->sc, bs->cur, level);
484+
/*
485+
* Otherwise, only the root level is allowed to have fewer than minrecs
486+
* records or keyptrs.
487+
*/
488+
if (level < root_level)
489+
xchk_btree_set_corrupt(bs->sc, cur, level);
481490
}
482491

483492
/*

fs/xfs/scrub/dir.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -558,14 +558,27 @@ xchk_directory_leaf1_bestfree(
558558
/* Check all the bestfree entries. */
559559
for (i = 0; i < bestcount; i++, bestp++) {
560560
best = be16_to_cpu(*bestp);
561-
if (best == NULLDATAOFF)
562-
continue;
563561
error = xfs_dir3_data_read(sc->tp, sc->ip,
564-
i * args->geo->fsbcount, 0, &dbp);
562+
xfs_dir2_db_to_da(args->geo, i),
563+
XFS_DABUF_MAP_HOLE_OK,
564+
&dbp);
565565
if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
566566
&error))
567567
break;
568-
xchk_directory_check_freesp(sc, lblk, dbp, best);
568+
569+
if (!dbp) {
570+
if (best != NULLDATAOFF) {
571+
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
572+
lblk);
573+
break;
574+
}
575+
continue;
576+
}
577+
578+
if (best == NULLDATAOFF)
579+
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
580+
else
581+
xchk_directory_check_freesp(sc, lblk, dbp, best);
569582
xfs_trans_brelse(sc->tp, dbp);
570583
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
571584
break;

fs/xfs/xfs_iomap.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,23 @@ xfs_ilock_for_iomap(
706706
return 0;
707707
}
708708

709+
/*
710+
* Check that the imap we are going to return to the caller spans the entire
711+
* range that the caller requested for the IO.
712+
*/
713+
static bool
714+
imap_spans_range(
715+
struct xfs_bmbt_irec *imap,
716+
xfs_fileoff_t offset_fsb,
717+
xfs_fileoff_t end_fsb)
718+
{
719+
if (imap->br_startoff > offset_fsb)
720+
return false;
721+
if (imap->br_startoff + imap->br_blockcount < end_fsb)
722+
return false;
723+
return true;
724+
}
725+
709726
static int
710727
xfs_direct_write_iomap_begin(
711728
struct inode *inode,
@@ -766,6 +783,18 @@ xfs_direct_write_iomap_begin(
766783
if (imap_needs_alloc(inode, flags, &imap, nimaps))
767784
goto allocate_blocks;
768785

786+
/*
787+
* NOWAIT IO needs to span the entire requested IO with a single map so
788+
* that we avoid partial IO failures due to the rest of the IO range not
789+
* covered by this map triggering an EAGAIN condition when it is
790+
* subsequently mapped and aborting the IO.
791+
*/
792+
if ((flags & IOMAP_NOWAIT) &&
793+
!imap_spans_range(&imap, offset_fsb, end_fsb)) {
794+
error = -EAGAIN;
795+
goto out_unlock;
796+
}
797+
769798
xfs_iunlock(ip, lockmode);
770799
trace_xfs_iomap_found(ip, offset, length, XFS_DATA_FORK, &imap);
771800
return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);

fs/xfs/xfs_iwalk.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ struct xfs_iwalk_ag {
5555
/* Where do we start the traversal? */
5656
xfs_ino_t startino;
5757

58+
/* What was the last inode number we saw when iterating the inobt? */
59+
xfs_ino_t lastino;
60+
5861
/* Array of inobt records we cache. */
5962
struct xfs_inobt_rec_incore *recs;
6063

@@ -301,6 +304,9 @@ xfs_iwalk_ag_start(
301304
if (XFS_IS_CORRUPT(mp, *has_more != 1))
302305
return -EFSCORRUPTED;
303306

307+
iwag->lastino = XFS_AGINO_TO_INO(mp, agno,
308+
irec->ir_startino + XFS_INODES_PER_CHUNK - 1);
309+
304310
/*
305311
* If the LE lookup yielded an inobt record before the cursor position,
306312
* skip it and see if there's another one after it.
@@ -347,15 +353,17 @@ xfs_iwalk_run_callbacks(
347353
struct xfs_mount *mp = iwag->mp;
348354
struct xfs_trans *tp = iwag->tp;
349355
struct xfs_inobt_rec_incore *irec;
350-
xfs_agino_t restart;
356+
xfs_agino_t next_agino;
351357
int error;
352358

359+
next_agino = XFS_INO_TO_AGINO(mp, iwag->lastino) + 1;
360+
353361
ASSERT(iwag->nr_recs > 0);
354362

355363
/* Delete cursor but remember the last record we cached... */
356364
xfs_iwalk_del_inobt(tp, curpp, agi_bpp, 0);
357365
irec = &iwag->recs[iwag->nr_recs - 1];
358-
restart = irec->ir_startino + XFS_INODES_PER_CHUNK - 1;
366+
ASSERT(next_agino == irec->ir_startino + XFS_INODES_PER_CHUNK);
359367

360368
error = xfs_iwalk_ag_recs(iwag);
361369
if (error)
@@ -372,7 +380,7 @@ xfs_iwalk_run_callbacks(
372380
if (error)
373381
return error;
374382

375-
return xfs_inobt_lookup(*curpp, restart, XFS_LOOKUP_GE, has_more);
383+
return xfs_inobt_lookup(*curpp, next_agino, XFS_LOOKUP_GE, has_more);
376384
}
377385

378386
/* Walk all inodes in a single AG, from @iwag->startino to the end of the AG. */
@@ -396,6 +404,7 @@ xfs_iwalk_ag(
396404

397405
while (!error && has_more) {
398406
struct xfs_inobt_rec_incore *irec;
407+
xfs_ino_t rec_fsino;
399408

400409
cond_resched();
401410
if (xfs_pwork_want_abort(&iwag->pwork))
@@ -407,6 +416,15 @@ xfs_iwalk_ag(
407416
if (error || !has_more)
408417
break;
409418

419+
/* Make sure that we always move forward. */
420+
rec_fsino = XFS_AGINO_TO_INO(mp, agno, irec->ir_startino);
421+
if (iwag->lastino != NULLFSINO &&
422+
XFS_IS_CORRUPT(mp, iwag->lastino >= rec_fsino)) {
423+
error = -EFSCORRUPTED;
424+
goto out;
425+
}
426+
iwag->lastino = rec_fsino + XFS_INODES_PER_CHUNK - 1;
427+
410428
/* No allocated inodes in this chunk; skip it. */
411429
if (iwag->skip_empty && irec->ir_freecount == irec->ir_count) {
412430
error = xfs_btree_increment(cur, 0, &has_more);
@@ -535,6 +553,7 @@ xfs_iwalk(
535553
.trim_start = 1,
536554
.skip_empty = 1,
537555
.pwork = XFS_PWORK_SINGLE_THREADED,
556+
.lastino = NULLFSINO,
538557
};
539558
xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);
540559
int error;
@@ -623,6 +642,7 @@ xfs_iwalk_threaded(
623642
iwag->data = data;
624643
iwag->startino = startino;
625644
iwag->sz_recs = xfs_iwalk_prefetch(inode_records);
645+
iwag->lastino = NULLFSINO;
626646
xfs_pwork_queue(&pctl, &iwag->pwork);
627647
startino = XFS_AGINO_TO_INO(mp, agno + 1, 0);
628648
if (flags & XFS_INOBT_WALK_SAME_AG)
@@ -696,6 +716,7 @@ xfs_inobt_walk(
696716
.startino = startino,
697717
.sz_recs = xfs_inobt_walk_prefetch(inobt_records),
698718
.pwork = XFS_PWORK_SINGLE_THREADED,
719+
.lastino = NULLFSINO,
699720
};
700721
xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);
701722
int error;

fs/xfs/xfs_mount.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,25 @@ xfs_initialize_perag(
194194
}
195195

196196
pag = kmem_zalloc(sizeof(*pag), KM_MAYFAIL);
197-
if (!pag)
197+
if (!pag) {
198+
error = -ENOMEM;
198199
goto out_unwind_new_pags;
200+
}
199201
pag->pag_agno = index;
200202
pag->pag_mount = mp;
201203
spin_lock_init(&pag->pag_ici_lock);
202204
INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC);
203-
if (xfs_buf_hash_init(pag))
205+
206+
error = xfs_buf_hash_init(pag);
207+
if (error)
204208
goto out_free_pag;
205209
init_waitqueue_head(&pag->pagb_wait);
206210
spin_lock_init(&pag->pagb_lock);
207211
pag->pagb_count = 0;
208212
pag->pagb_tree = RB_ROOT;
209213

210-
if (radix_tree_preload(GFP_NOFS))
214+
error = radix_tree_preload(GFP_NOFS);
215+
if (error)
211216
goto out_hash_destroy;
212217

213218
spin_lock(&mp->m_perag_lock);

0 commit comments

Comments
 (0)