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

Merge tag 'xfs-4.17-fixes-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
"Here are a few more bug fixes for xfs for 4.17-rc4. Most of them are
fixes for bad behavior.

This series has been run through a full xfstests run during LSF and
through a quick xfstests run against this morning's master, with no
major failures reported.

Summary:

- Enhance inode fork verifiers to prevent loading of corrupted
metadata.

- Fix a crash when we try to convert extents format inodes to btree
format, we run out of space, but forget to revert the in-core state
changes.

- Fix file size checks when doing INSERT_RANGE that could cause files
to end up negative size if there previously was an extent mapped at
s_maxbytes.

- Fix a bug when doing a remove-then-add ATTR_REPLACE xattr update
where we forget to clear ATTR_REPLACE after the remove, which
causes the attr to be lost and the fs to shut down due to (what it
thinks is) inconsistent in-core state"

* tag 'xfs-4.17-fixes-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
xfs: don't fail when converting shortform attr to long form during ATTR_REPLACE
xfs: prevent creating negative-sized file via INSERT_RANGE
xfs: set format back to extents if xfs_bmap_extents_to_btree
xfs: enhance dinode verifier

+42 -6
+8 -1
fs/xfs/libxfs/xfs_attr.c
··· 511 511 if (args->flags & ATTR_CREATE) 512 512 return retval; 513 513 retval = xfs_attr_shortform_remove(args); 514 - ASSERT(retval == 0); 514 + if (retval) 515 + return retval; 516 + /* 517 + * Since we have removed the old attr, clear ATTR_REPLACE so 518 + * that the leaf format add routine won't trip over the attr 519 + * not being around. 520 + */ 521 + args->flags &= ~ATTR_REPLACE; 515 522 } 516 523 517 524 if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
+4
fs/xfs/libxfs/xfs_bmap.c
··· 725 725 *logflagsp = 0; 726 726 if ((error = xfs_alloc_vextent(&args))) { 727 727 xfs_iroot_realloc(ip, -1, whichfork); 728 + ASSERT(ifp->if_broot == NULL); 729 + XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); 728 730 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 729 731 return error; 730 732 } 731 733 732 734 if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) { 733 735 xfs_iroot_realloc(ip, -1, whichfork); 736 + ASSERT(ifp->if_broot == NULL); 737 + XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); 734 738 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 735 739 return -ENOSPC; 736 740 }
+21
fs/xfs/libxfs/xfs_inode_buf.c
··· 466 466 return __this_address; 467 467 if (di_size > XFS_DFORK_DSIZE(dip, mp)) 468 468 return __this_address; 469 + if (dip->di_nextents) 470 + return __this_address; 469 471 /* fall through */ 470 472 case XFS_DINODE_FMT_EXTENTS: 471 473 case XFS_DINODE_FMT_BTREE: ··· 486 484 if (XFS_DFORK_Q(dip)) { 487 485 switch (dip->di_aformat) { 488 486 case XFS_DINODE_FMT_LOCAL: 487 + if (dip->di_anextents) 488 + return __this_address; 489 + /* fall through */ 489 490 case XFS_DINODE_FMT_EXTENTS: 490 491 case XFS_DINODE_FMT_BTREE: 491 492 break; 492 493 default: 493 494 return __this_address; 494 495 } 496 + } else { 497 + /* 498 + * If there is no fork offset, this may be a freshly-made inode 499 + * in a new disk cluster, in which case di_aformat is zeroed. 500 + * Otherwise, such an inode must be in EXTENTS format; this goes 501 + * for freed inodes as well. 502 + */ 503 + switch (dip->di_aformat) { 504 + case 0: 505 + case XFS_DINODE_FMT_EXTENTS: 506 + break; 507 + default: 508 + return __this_address; 509 + } 510 + if (dip->di_anextents) 511 + return __this_address; 495 512 } 496 513 497 514 /* only version 3 or greater inodes are extensively verified here */
+9 -5
fs/xfs/xfs_file.c
··· 778 778 if (error) 779 779 goto out_unlock; 780 780 } else if (mode & FALLOC_FL_INSERT_RANGE) { 781 - unsigned int blksize_mask = i_blocksize(inode) - 1; 781 + unsigned int blksize_mask = i_blocksize(inode) - 1; 782 + loff_t isize = i_size_read(inode); 782 783 783 - new_size = i_size_read(inode) + len; 784 784 if (offset & blksize_mask || len & blksize_mask) { 785 785 error = -EINVAL; 786 786 goto out_unlock; 787 787 } 788 788 789 - /* check the new inode size does not wrap through zero */ 790 - if (new_size > inode->i_sb->s_maxbytes) { 789 + /* 790 + * New inode size must not exceed ->s_maxbytes, accounting for 791 + * possible signed overflow. 792 + */ 793 + if (inode->i_sb->s_maxbytes - isize < len) { 791 794 error = -EFBIG; 792 795 goto out_unlock; 793 796 } 797 + new_size = isize + len; 794 798 795 799 /* Offset should be less than i_size */ 796 - if (offset >= i_size_read(inode)) { 800 + if (offset >= isize) { 797 801 error = -EINVAL; 798 802 goto out_unlock; 799 803 }