Commit f2125992 authored by Linus Torvalds's avatar Linus Torvalds

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
parents fff75eb2 7b38460d
...@@ -511,7 +511,14 @@ xfs_attr_shortform_addname(xfs_da_args_t *args) ...@@ -511,7 +511,14 @@ xfs_attr_shortform_addname(xfs_da_args_t *args)
if (args->flags & ATTR_CREATE) if (args->flags & ATTR_CREATE)
return retval; return retval;
retval = xfs_attr_shortform_remove(args); retval = xfs_attr_shortform_remove(args);
ASSERT(retval == 0); if (retval)
return retval;
/*
* Since we have removed the old attr, clear ATTR_REPLACE so
* that the leaf format add routine won't trip over the attr
* not being around.
*/
args->flags &= ~ATTR_REPLACE;
} }
if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX || if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
......
...@@ -725,12 +725,16 @@ xfs_bmap_extents_to_btree( ...@@ -725,12 +725,16 @@ xfs_bmap_extents_to_btree(
*logflagsp = 0; *logflagsp = 0;
if ((error = xfs_alloc_vextent(&args))) { if ((error = xfs_alloc_vextent(&args))) {
xfs_iroot_realloc(ip, -1, whichfork); xfs_iroot_realloc(ip, -1, whichfork);
ASSERT(ifp->if_broot == NULL);
XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
return error; return error;
} }
if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) { if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) {
xfs_iroot_realloc(ip, -1, whichfork); xfs_iroot_realloc(ip, -1, whichfork);
ASSERT(ifp->if_broot == NULL);
XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
return -ENOSPC; return -ENOSPC;
} }
......
...@@ -466,6 +466,8 @@ xfs_dinode_verify( ...@@ -466,6 +466,8 @@ xfs_dinode_verify(
return __this_address; return __this_address;
if (di_size > XFS_DFORK_DSIZE(dip, mp)) if (di_size > XFS_DFORK_DSIZE(dip, mp))
return __this_address; return __this_address;
if (dip->di_nextents)
return __this_address;
/* fall through */ /* fall through */
case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_EXTENTS:
case XFS_DINODE_FMT_BTREE: case XFS_DINODE_FMT_BTREE:
...@@ -484,12 +486,31 @@ xfs_dinode_verify( ...@@ -484,12 +486,31 @@ xfs_dinode_verify(
if (XFS_DFORK_Q(dip)) { if (XFS_DFORK_Q(dip)) {
switch (dip->di_aformat) { switch (dip->di_aformat) {
case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_LOCAL:
if (dip->di_anextents)
return __this_address;
/* fall through */
case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_EXTENTS:
case XFS_DINODE_FMT_BTREE: case XFS_DINODE_FMT_BTREE:
break; break;
default: default:
return __this_address; return __this_address;
} }
} else {
/*
* If there is no fork offset, this may be a freshly-made inode
* in a new disk cluster, in which case di_aformat is zeroed.
* Otherwise, such an inode must be in EXTENTS format; this goes
* for freed inodes as well.
*/
switch (dip->di_aformat) {
case 0:
case XFS_DINODE_FMT_EXTENTS:
break;
default:
return __this_address;
}
if (dip->di_anextents)
return __this_address;
} }
/* only version 3 or greater inodes are extensively verified here */ /* only version 3 or greater inodes are extensively verified here */
......
...@@ -778,22 +778,26 @@ xfs_file_fallocate( ...@@ -778,22 +778,26 @@ xfs_file_fallocate(
if (error) if (error)
goto out_unlock; goto out_unlock;
} else if (mode & FALLOC_FL_INSERT_RANGE) { } else if (mode & FALLOC_FL_INSERT_RANGE) {
unsigned int blksize_mask = i_blocksize(inode) - 1; unsigned int blksize_mask = i_blocksize(inode) - 1;
loff_t isize = i_size_read(inode);
new_size = i_size_read(inode) + len;
if (offset & blksize_mask || len & blksize_mask) { if (offset & blksize_mask || len & blksize_mask) {
error = -EINVAL; error = -EINVAL;
goto out_unlock; goto out_unlock;
} }
/* check the new inode size does not wrap through zero */ /*
if (new_size > inode->i_sb->s_maxbytes) { * New inode size must not exceed ->s_maxbytes, accounting for
* possible signed overflow.
*/
if (inode->i_sb->s_maxbytes - isize < len) {
error = -EFBIG; error = -EFBIG;
goto out_unlock; goto out_unlock;
} }
new_size = isize + len;
/* Offset should be less than i_size */ /* Offset should be less than i_size */
if (offset >= i_size_read(inode)) { if (offset >= isize) {
error = -EINVAL; error = -EINVAL;
goto out_unlock; goto out_unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment