Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfs: track cow/shared record domains explicitly in xfs_refcount_irec

Just prior to committing the reflink code into upstream, the xfs
maintainer at the time requested that I find a way to shard the refcount
records into two domains -- one for records tracking shared extents, and
a second for tracking CoW staging extents. The idea here was to
minimize mount time CoW reclamation by pushing all the CoW records to
the right edge of the keyspace, and it was accomplished by setting the
upper bit in rc_startblock. We don't allow AGs to have more than 2^31
blocks, so the bit was free.

Unfortunately, this was a very late addition to the codebase, so most of
the refcount record processing code still treats rc_startblock as a u32
and pays no attention to whether or not the upper bit (the cow flag) is
set. This is a weakness is theoretically exploitable, since we're not
fully validating the incoming metadata records.

Fuzzing demonstrates practical exploits of this weakness. If the cow
flag of a node block key record is corrupted, a lookup operation can go
to the wrong record block and start returning records from the wrong
cow/shared domain. This causes the math to go all wrong (since cow
domain is still implicit in the upper bit of rc_startblock) and we can
crash the kernel by tricking xfs into jumping into a nonexistent AG and
tripping over xfs_perag_get(mp, <nonexistent AG>) returning NULL.

To fix this, start tracking the domain as an explicit part of struct
xfs_refcount_irec, adjust all refcount functions to check the domain
of a returned record, and alter the function definitions to accept them
where necessary.

Found by fuzzing keys[2].cowflag = add in xfs/464.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>

+151 -67
+99 -47
fs/xfs/libxfs/xfs_refcount.c
··· 46 46 int 47 47 xfs_refcount_lookup_le( 48 48 struct xfs_btree_cur *cur, 49 + enum xfs_refc_domain domain, 49 50 xfs_agblock_t bno, 50 51 int *stat) 51 52 { 52 - trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, 53 + trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, 54 + xfs_refcount_encode_startblock(bno, domain), 53 55 XFS_LOOKUP_LE); 54 56 cur->bc_rec.rc.rc_startblock = bno; 55 57 cur->bc_rec.rc.rc_blockcount = 0; 58 + cur->bc_rec.rc.rc_domain = domain; 56 59 return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat); 57 60 } 58 61 ··· 66 63 int 67 64 xfs_refcount_lookup_ge( 68 65 struct xfs_btree_cur *cur, 66 + enum xfs_refc_domain domain, 69 67 xfs_agblock_t bno, 70 68 int *stat) 71 69 { 72 - trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, 70 + trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, 71 + xfs_refcount_encode_startblock(bno, domain), 73 72 XFS_LOOKUP_GE); 74 73 cur->bc_rec.rc.rc_startblock = bno; 75 74 cur->bc_rec.rc.rc_blockcount = 0; 75 + cur->bc_rec.rc.rc_domain = domain; 76 76 return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat); 77 77 } 78 78 ··· 86 80 int 87 81 xfs_refcount_lookup_eq( 88 82 struct xfs_btree_cur *cur, 83 + enum xfs_refc_domain domain, 89 84 xfs_agblock_t bno, 90 85 int *stat) 91 86 { 92 - trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, bno, 87 + trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_ag.pag->pag_agno, 88 + xfs_refcount_encode_startblock(bno, domain), 93 89 XFS_LOOKUP_LE); 94 90 cur->bc_rec.rc.rc_startblock = bno; 95 91 cur->bc_rec.rc.rc_blockcount = 0; 92 + cur->bc_rec.rc.rc_domain = domain; 96 93 return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat); 97 94 } 98 95 ··· 105 96 const union xfs_btree_rec *rec, 106 97 struct xfs_refcount_irec *irec) 107 98 { 108 - irec->rc_startblock = be32_to_cpu(rec->refc.rc_startblock); 99 + uint32_t start; 100 + 101 + start = be32_to_cpu(rec->refc.rc_startblock); 102 + if (start & XFS_REFC_COW_START) { 103 + start &= ~XFS_REFC_COW_START; 104 + irec->rc_domain = XFS_REFC_DOMAIN_COW; 105 + } else { 106 + irec->rc_domain = XFS_REFC_DOMAIN_SHARED; 107 + } 108 + 109 + irec->rc_startblock = start; 109 110 irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount); 110 111 irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount); 111 112 } ··· 133 114 struct xfs_perag *pag = cur->bc_ag.pag; 134 115 union xfs_btree_rec *rec; 135 116 int error; 136 - xfs_agblock_t realstart; 137 117 138 118 error = xfs_btree_get_rec(cur, &rec, stat); 139 119 if (error || !*stat) ··· 142 124 if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN) 143 125 goto out_bad_rec; 144 126 145 - /* handle special COW-staging state */ 146 - realstart = irec->rc_startblock; 147 - if (realstart & XFS_REFC_COW_START) { 148 - if (irec->rc_refcount != 1) 149 - goto out_bad_rec; 150 - realstart &= ~XFS_REFC_COW_START; 151 - } else if (irec->rc_refcount < 2) { 127 + /* handle special COW-staging domain */ 128 + if (irec->rc_domain == XFS_REFC_DOMAIN_COW && irec->rc_refcount != 1) 152 129 goto out_bad_rec; 153 - } 130 + if (irec->rc_domain == XFS_REFC_DOMAIN_SHARED && irec->rc_refcount < 2) 131 + goto out_bad_rec; 154 132 155 133 /* check for valid extent range, including overflow */ 156 - if (!xfs_verify_agbext(pag, realstart, irec->rc_blockcount)) 134 + if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount)) 157 135 goto out_bad_rec; 158 136 159 137 if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT) ··· 179 165 struct xfs_refcount_irec *irec) 180 166 { 181 167 union xfs_btree_rec rec; 168 + uint32_t start; 182 169 int error; 183 170 184 171 trace_xfs_refcount_update(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec); 185 - rec.refc.rc_startblock = cpu_to_be32(irec->rc_startblock); 172 + 173 + start = xfs_refcount_encode_startblock(irec->rc_startblock, 174 + irec->rc_domain); 175 + rec.refc.rc_startblock = cpu_to_be32(start); 186 176 rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount); 187 177 rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount); 178 + 188 179 error = xfs_btree_update(cur, &rec); 189 180 if (error) 190 181 trace_xfs_refcount_update_error(cur->bc_mp, ··· 211 192 int error; 212 193 213 194 trace_xfs_refcount_insert(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec); 195 + 214 196 cur->bc_rec.rc.rc_startblock = irec->rc_startblock; 215 197 cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount; 216 198 cur->bc_rec.rc.rc_refcount = irec->rc_refcount; 199 + cur->bc_rec.rc.rc_domain = irec->rc_domain; 200 + 217 201 error = xfs_btree_insert(cur, i); 218 202 if (error) 219 203 goto out_error; ··· 262 240 } 263 241 if (error) 264 242 goto out_error; 265 - error = xfs_refcount_lookup_ge(cur, irec.rc_startblock, &found_rec); 243 + error = xfs_refcount_lookup_ge(cur, irec.rc_domain, irec.rc_startblock, 244 + &found_rec); 266 245 out_error: 267 246 if (error) 268 247 trace_xfs_refcount_delete_error(cur->bc_mp, ··· 362 339 STATIC int 363 340 xfs_refcount_split_extent( 364 341 struct xfs_btree_cur *cur, 342 + enum xfs_refc_domain domain, 365 343 xfs_agblock_t agbno, 366 344 bool *shape_changed) 367 345 { ··· 371 347 int error; 372 348 373 349 *shape_changed = false; 374 - error = xfs_refcount_lookup_le(cur, agbno, &found_rec); 350 + error = xfs_refcount_lookup_le(cur, domain, agbno, &found_rec); 375 351 if (error) 376 352 goto out_error; 377 353 if (!found_rec) ··· 443 419 * call removes the center and the second one removes the right 444 420 * extent. 445 421 */ 446 - error = xfs_refcount_lookup_ge(cur, center->rc_startblock, 447 - &found_rec); 422 + error = xfs_refcount_lookup_ge(cur, center->rc_domain, 423 + center->rc_startblock, &found_rec); 448 424 if (error) 449 425 goto out_error; 450 426 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 471 447 } 472 448 473 449 /* Enlarge the left extent. */ 474 - error = xfs_refcount_lookup_le(cur, left->rc_startblock, 475 - &found_rec); 450 + error = xfs_refcount_lookup_le(cur, left->rc_domain, 451 + left->rc_startblock, &found_rec); 476 452 if (error) 477 453 goto out_error; 478 454 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 513 489 514 490 /* If the extent at agbno (cleft) wasn't synthesized, remove it. */ 515 491 if (cleft->rc_refcount > 1) { 516 - error = xfs_refcount_lookup_le(cur, cleft->rc_startblock, 517 - &found_rec); 492 + error = xfs_refcount_lookup_le(cur, cleft->rc_domain, 493 + cleft->rc_startblock, &found_rec); 518 494 if (error) 519 495 goto out_error; 520 496 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 532 508 } 533 509 534 510 /* Enlarge the left extent. */ 535 - error = xfs_refcount_lookup_le(cur, left->rc_startblock, 536 - &found_rec); 511 + error = xfs_refcount_lookup_le(cur, left->rc_domain, 512 + left->rc_startblock, &found_rec); 537 513 if (error) 538 514 goto out_error; 539 515 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 577 553 * remove it. 578 554 */ 579 555 if (cright->rc_refcount > 1) { 580 - error = xfs_refcount_lookup_le(cur, cright->rc_startblock, 581 - &found_rec); 556 + error = xfs_refcount_lookup_le(cur, cright->rc_domain, 557 + cright->rc_startblock, &found_rec); 582 558 if (error) 583 559 goto out_error; 584 560 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 596 572 } 597 573 598 574 /* Enlarge the right extent. */ 599 - error = xfs_refcount_lookup_le(cur, right->rc_startblock, 600 - &found_rec); 575 + error = xfs_refcount_lookup_le(cur, right->rc_domain, 576 + right->rc_startblock, &found_rec); 601 577 if (error) 602 578 goto out_error; 603 579 if (XFS_IS_CORRUPT(cur->bc_mp, found_rec != 1)) { ··· 636 612 int flags) 637 613 { 638 614 struct xfs_refcount_irec tmp; 615 + enum xfs_refc_domain domain; 639 616 int error; 640 617 int found_rec; 641 618 619 + if (flags & XFS_FIND_RCEXT_SHARED) 620 + domain = XFS_REFC_DOMAIN_SHARED; 621 + else 622 + domain = XFS_REFC_DOMAIN_COW; 623 + 642 624 left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK; 643 - error = xfs_refcount_lookup_le(cur, agbno - 1, &found_rec); 625 + error = xfs_refcount_lookup_le(cur, domain, agbno - 1, &found_rec); 644 626 if (error) 645 627 goto out_error; 646 628 if (!found_rec) ··· 697 667 cleft->rc_blockcount = min(aglen, 698 668 tmp.rc_startblock - agbno); 699 669 cleft->rc_refcount = 1; 670 + cleft->rc_domain = domain; 700 671 } 701 672 } else { 702 673 /* ··· 707 676 cleft->rc_startblock = agbno; 708 677 cleft->rc_blockcount = aglen; 709 678 cleft->rc_refcount = 1; 679 + cleft->rc_domain = domain; 710 680 } 711 681 trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno, 712 682 left, cleft, agbno); ··· 733 701 int flags) 734 702 { 735 703 struct xfs_refcount_irec tmp; 704 + enum xfs_refc_domain domain; 736 705 int error; 737 706 int found_rec; 738 707 708 + if (flags & XFS_FIND_RCEXT_SHARED) 709 + domain = XFS_REFC_DOMAIN_SHARED; 710 + else 711 + domain = XFS_REFC_DOMAIN_COW; 712 + 739 713 right->rc_startblock = cright->rc_startblock = NULLAGBLOCK; 740 - error = xfs_refcount_lookup_ge(cur, agbno + aglen, &found_rec); 714 + error = xfs_refcount_lookup_ge(cur, domain, agbno + aglen, &found_rec); 741 715 if (error) 742 716 goto out_error; 743 717 if (!found_rec) ··· 794 756 cright->rc_blockcount = right->rc_startblock - 795 757 cright->rc_startblock; 796 758 cright->rc_refcount = 1; 759 + cright->rc_domain = domain; 797 760 } 798 761 } else { 799 762 /* ··· 804 765 cright->rc_startblock = agbno; 805 766 cright->rc_blockcount = aglen; 806 767 cright->rc_refcount = 1; 768 + cright->rc_domain = domain; 807 769 } 808 770 trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_ag.pag->pag_agno, 809 771 cright, right, agbno + aglen); ··· 969 929 if (*aglen == 0) 970 930 return 0; 971 931 972 - error = xfs_refcount_lookup_ge(cur, *agbno, &found_rec); 932 + error = xfs_refcount_lookup_ge(cur, XFS_REFC_DOMAIN_SHARED, *agbno, 933 + &found_rec); 973 934 if (error) 974 935 goto out_error; 975 936 ··· 982 941 ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks; 983 942 ext.rc_blockcount = 0; 984 943 ext.rc_refcount = 0; 944 + ext.rc_domain = XFS_REFC_DOMAIN_SHARED; 985 945 } 986 946 987 947 /* ··· 995 953 tmp.rc_blockcount = min(*aglen, 996 954 ext.rc_startblock - *agbno); 997 955 tmp.rc_refcount = 1 + adj; 956 + tmp.rc_domain = XFS_REFC_DOMAIN_SHARED; 957 + 998 958 trace_xfs_refcount_modify_extent(cur->bc_mp, 999 959 cur->bc_ag.pag->pag_agno, &tmp); 1000 960 ··· 1031 987 break; 1032 988 1033 989 /* Move the cursor to the start of ext. */ 1034 - error = xfs_refcount_lookup_ge(cur, *agbno, 990 + error = xfs_refcount_lookup_ge(cur, 991 + XFS_REFC_DOMAIN_SHARED, *agbno, 1035 992 &found_rec); 1036 993 if (error) 1037 994 goto out_error; ··· 1125 1080 /* 1126 1081 * Ensure that no rcextents cross the boundary of the adjustment range. 1127 1082 */ 1128 - error = xfs_refcount_split_extent(cur, agbno, &shape_changed); 1083 + error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_SHARED, 1084 + agbno, &shape_changed); 1129 1085 if (error) 1130 1086 goto out_error; 1131 1087 if (shape_changed) 1132 1088 shape_changes++; 1133 1089 1134 - error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed); 1090 + error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_SHARED, 1091 + agbno + aglen, &shape_changed); 1135 1092 if (error) 1136 1093 goto out_error; 1137 1094 if (shape_changed) ··· 1398 1351 *flen = 0; 1399 1352 1400 1353 /* Try to find a refcount extent that crosses the start */ 1401 - error = xfs_refcount_lookup_le(cur, agbno, &have); 1354 + error = xfs_refcount_lookup_le(cur, XFS_REFC_DOMAIN_SHARED, agbno, 1355 + &have); 1402 1356 if (error) 1403 1357 goto out_error; 1404 1358 if (!have) { ··· 1547 1499 return 0; 1548 1500 1549 1501 /* Find any overlapping refcount records */ 1550 - error = xfs_refcount_lookup_ge(cur, agbno, &found_rec); 1502 + error = xfs_refcount_lookup_ge(cur, XFS_REFC_DOMAIN_COW, agbno, 1503 + &found_rec); 1551 1504 if (error) 1552 1505 goto out_error; 1553 1506 error = xfs_refcount_get_rec(cur, &ext, &found_rec); 1554 1507 if (error) 1555 1508 goto out_error; 1556 1509 if (!found_rec) { 1557 - ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks + 1558 - XFS_REFC_COW_START; 1510 + ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks; 1559 1511 ext.rc_blockcount = 0; 1560 1512 ext.rc_refcount = 0; 1513 + ext.rc_domain = XFS_REFC_DOMAIN_COW; 1561 1514 } 1562 1515 1563 1516 switch (adj) { ··· 1573 1524 tmp.rc_startblock = agbno; 1574 1525 tmp.rc_blockcount = aglen; 1575 1526 tmp.rc_refcount = 1; 1527 + tmp.rc_domain = XFS_REFC_DOMAIN_COW; 1528 + 1576 1529 trace_xfs_refcount_modify_extent(cur->bc_mp, 1577 1530 cur->bc_ag.pag->pag_agno, &tmp); 1578 1531 ··· 1637 1586 bool shape_changed; 1638 1587 int error; 1639 1588 1640 - agbno += XFS_REFC_COW_START; 1641 - 1642 1589 /* 1643 1590 * Ensure that no rcextents cross the boundary of the adjustment range. 1644 1591 */ 1645 - error = xfs_refcount_split_extent(cur, agbno, &shape_changed); 1592 + error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_COW, 1593 + agbno, &shape_changed); 1646 1594 if (error) 1647 1595 goto out_error; 1648 1596 1649 - error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed); 1597 + error = xfs_refcount_split_extent(cur, XFS_REFC_DOMAIN_COW, 1598 + agbno + aglen, &shape_changed); 1650 1599 if (error) 1651 1600 goto out_error; 1652 1601 ··· 1782 1731 union xfs_btree_irec low; 1783 1732 union xfs_btree_irec high; 1784 1733 xfs_fsblock_t fsb; 1785 - xfs_agblock_t agbno; 1786 1734 int error; 1787 1735 1788 1736 if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START) ··· 1811 1761 /* Find all the leftover CoW staging extents. */ 1812 1762 memset(&low, 0, sizeof(low)); 1813 1763 memset(&high, 0, sizeof(high)); 1814 - low.rc.rc_startblock = XFS_REFC_COW_START; 1764 + low.rc.rc_domain = high.rc.rc_domain = XFS_REFC_DOMAIN_COW; 1815 1765 high.rc.rc_startblock = -1U; 1816 1766 error = xfs_btree_query_range(cur, &low, &high, 1817 1767 xfs_refcount_recover_extent, &debris); ··· 1832 1782 &rr->rr_rrec); 1833 1783 1834 1784 /* Free the orphan record */ 1835 - agbno = rr->rr_rrec.rc_startblock - XFS_REFC_COW_START; 1836 - fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno, agbno); 1785 + fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno, 1786 + rr->rr_rrec.rc_startblock); 1837 1787 xfs_refcount_free_cow_extent(tp, fsb, 1838 1788 rr->rr_rrec.rc_blockcount); 1839 1789 ··· 1864 1814 int 1865 1815 xfs_refcount_has_record( 1866 1816 struct xfs_btree_cur *cur, 1817 + enum xfs_refc_domain domain, 1867 1818 xfs_agblock_t bno, 1868 1819 xfs_extlen_t len, 1869 1820 bool *exists) ··· 1876 1825 low.rc.rc_startblock = bno; 1877 1826 memset(&high, 0xFF, sizeof(high)); 1878 1827 high.rc.rc_startblock = bno + len - 1; 1828 + low.rc.rc_domain = high.rc.rc_domain = domain; 1879 1829 1880 1830 return xfs_btree_has_record(cur, &low, &high, exists); 1881 1831 }
+24 -4
fs/xfs/libxfs/xfs_refcount.h
··· 14 14 struct xfs_refcount_irec; 15 15 16 16 extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur, 17 - xfs_agblock_t bno, int *stat); 17 + enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat); 18 18 extern int xfs_refcount_lookup_ge(struct xfs_btree_cur *cur, 19 - xfs_agblock_t bno, int *stat); 19 + enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat); 20 20 extern int xfs_refcount_lookup_eq(struct xfs_btree_cur *cur, 21 - xfs_agblock_t bno, int *stat); 21 + enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat); 22 22 extern int xfs_refcount_get_rec(struct xfs_btree_cur *cur, 23 23 struct xfs_refcount_irec *irec, int *stat); 24 + 25 + static inline uint32_t 26 + xfs_refcount_encode_startblock( 27 + xfs_agblock_t startblock, 28 + enum xfs_refc_domain domain) 29 + { 30 + uint32_t start; 31 + 32 + /* 33 + * low level btree operations need to handle the generic btree range 34 + * query functions (which set rc_domain == -1U), so we check that the 35 + * domain is /not/ shared. 36 + */ 37 + start = startblock & ~XFS_REFC_COW_START; 38 + if (domain != XFS_REFC_DOMAIN_SHARED) 39 + start |= XFS_REFC_COW_START; 40 + 41 + return start; 42 + } 24 43 25 44 enum xfs_refcount_intent_type { 26 45 XFS_REFCOUNT_INCREASE = 1, ··· 98 79 #define XFS_REFCOUNT_ITEM_OVERHEAD 32 99 80 100 81 extern int xfs_refcount_has_record(struct xfs_btree_cur *cur, 101 - xfs_agblock_t bno, xfs_extlen_t len, bool *exists); 82 + enum xfs_refc_domain domain, xfs_agblock_t bno, 83 + xfs_extlen_t len, bool *exists); 102 84 union xfs_btree_rec; 103 85 extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec, 104 86 struct xfs_refcount_irec *irec);
+12 -3
fs/xfs/libxfs/xfs_refcount_btree.c
··· 13 13 #include "xfs_btree.h" 14 14 #include "xfs_btree_staging.h" 15 15 #include "xfs_refcount_btree.h" 16 + #include "xfs_refcount.h" 16 17 #include "xfs_alloc.h" 17 18 #include "xfs_error.h" 18 19 #include "xfs_trace.h" ··· 161 160 struct xfs_btree_cur *cur, 162 161 union xfs_btree_rec *rec) 163 162 { 164 - rec->refc.rc_startblock = cpu_to_be32(cur->bc_rec.rc.rc_startblock); 163 + const struct xfs_refcount_irec *irec = &cur->bc_rec.rc; 164 + uint32_t start; 165 + 166 + start = xfs_refcount_encode_startblock(irec->rc_startblock, 167 + irec->rc_domain); 168 + rec->refc.rc_startblock = cpu_to_be32(start); 165 169 rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount); 166 170 rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount); 167 171 } ··· 188 182 struct xfs_btree_cur *cur, 189 183 const union xfs_btree_key *key) 190 184 { 191 - struct xfs_refcount_irec *rec = &cur->bc_rec.rc; 192 185 const struct xfs_refcount_key *kp = &key->refc; 186 + const struct xfs_refcount_irec *irec = &cur->bc_rec.rc; 187 + uint32_t start; 193 188 194 - return (int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock; 189 + start = xfs_refcount_encode_startblock(irec->rc_startblock, 190 + irec->rc_domain); 191 + return (int64_t)be32_to_cpu(kp->rc_startblock) - start; 195 192 } 196 193 197 194 STATIC int64_t
+6
fs/xfs/libxfs/xfs_types.h
··· 166 166 xfs_exntst_t br_state; /* extent state */ 167 167 } xfs_bmbt_irec_t; 168 168 169 + enum xfs_refc_domain { 170 + XFS_REFC_DOMAIN_SHARED = 0, 171 + XFS_REFC_DOMAIN_COW, 172 + }; 173 + 169 174 struct xfs_refcount_irec { 170 175 xfs_agblock_t rc_startblock; /* starting block number */ 171 176 xfs_extlen_t rc_blockcount; /* count of free blocks */ 172 177 xfs_nlink_t rc_refcount; /* number of inodes linked here */ 178 + enum xfs_refc_domain rc_domain; /* shared or cow staging extent? */ 173 179 }; 174 180 175 181 #define XFS_RMAP_ATTR_FORK (1 << 0)
+10 -13
fs/xfs/scrub/refcount.c
··· 334 334 struct xfs_refcount_irec irec; 335 335 xfs_agblock_t *cow_blocks = bs->private; 336 336 struct xfs_perag *pag = bs->cur->bc_ag.pag; 337 - bool has_cowflag; 338 337 339 338 xfs_refcount_btrec_to_irec(rec, &irec); 340 339 341 340 /* Only CoW records can have refcount == 1. */ 342 - has_cowflag = (irec.rc_startblock & XFS_REFC_COW_START); 343 - if ((irec.rc_refcount == 1 && !has_cowflag) || 344 - (irec.rc_refcount != 1 && has_cowflag)) 341 + if (irec.rc_domain == XFS_REFC_DOMAIN_SHARED && irec.rc_refcount == 1) 345 342 xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 346 - if (has_cowflag) 343 + if (irec.rc_domain == XFS_REFC_DOMAIN_COW) { 344 + if (irec.rc_refcount != 1) 345 + xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 347 346 (*cow_blocks) += irec.rc_blockcount; 347 + } 348 348 349 349 /* Check the extent. */ 350 - irec.rc_startblock &= ~XFS_REFC_COW_START; 351 - 352 350 if (!xfs_verify_agbext(pag, irec.rc_startblock, irec.rc_blockcount)) 353 351 xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 354 352 ··· 417 419 xfs_extlen_t len) 418 420 { 419 421 struct xfs_refcount_irec rc; 420 - bool has_cowflag; 421 422 int has_refcount; 422 423 int error; 423 424 ··· 424 427 return; 425 428 426 429 /* Find the CoW staging extent. */ 427 - error = xfs_refcount_lookup_le(sc->sa.refc_cur, 428 - agbno + XFS_REFC_COW_START, &has_refcount); 430 + error = xfs_refcount_lookup_le(sc->sa.refc_cur, XFS_REFC_DOMAIN_COW, 431 + agbno, &has_refcount); 429 432 if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) 430 433 return; 431 434 if (!has_refcount) { ··· 442 445 } 443 446 444 447 /* CoW flag must be set, refcount must be 1. */ 445 - has_cowflag = (rc.rc_startblock & XFS_REFC_COW_START); 446 - if (!has_cowflag || rc.rc_refcount != 1) 448 + if (rc.rc_domain != XFS_REFC_DOMAIN_COW || rc.rc_refcount != 1) 447 449 xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); 448 450 449 451 /* Must be at least as long as what was passed in */ ··· 466 470 if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm)) 467 471 return; 468 472 469 - error = xfs_refcount_has_record(sc->sa.refc_cur, agbno, len, &shared); 473 + error = xfs_refcount_has_record(sc->sa.refc_cur, XFS_REFC_DOMAIN_SHARED, 474 + agbno, len, &shared); 470 475 if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) 471 476 return; 472 477 if (shared)