Commit 59c376d6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'iomap-6.6-fixes-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull iomap fixes from Darrick Wong:

 - Return EIO on bad inputs to iomap_to_bh instead of BUGging, to deal
   less poorly with block device io racing with block device resizing

 - Fix a stale page data exposure bug introduced in 6.6-rc1 when
   unsharing a file range that is not in the page cache

* tag 'iomap-6.6-fixes-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  iomap: convert iomap_unshare_iter to use large folios
  iomap: don't skip reading in !uptodate folios when unsharing a range
  iomap: handle error conditions more gracefully in iomap_to_bh
parents d90b0276 a5f31a50
Loading
Loading
Loading
Loading
+14 −11
Original line number Diff line number Diff line
@@ -2011,7 +2011,7 @@ void folio_zero_new_buffers(struct folio *folio, size_t from, size_t to)
}
EXPORT_SYMBOL(folio_zero_new_buffers);

static void
static int
iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
		const struct iomap *iomap)
{
@@ -2025,7 +2025,8 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
	 * current block, then do not map the buffer and let the caller
	 * handle it.
	 */
	BUG_ON(offset >= iomap->offset + iomap->length);
	if (offset >= iomap->offset + iomap->length)
		return -EIO;

	switch (iomap->type) {
	case IOMAP_HOLE:
@@ -2037,7 +2038,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
		if (!buffer_uptodate(bh) ||
		    (offset >= i_size_read(inode)))
			set_buffer_new(bh);
		break;
		return 0;
	case IOMAP_DELALLOC:
		if (!buffer_uptodate(bh) ||
		    (offset >= i_size_read(inode)))
@@ -2045,7 +2046,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
		set_buffer_uptodate(bh);
		set_buffer_mapped(bh);
		set_buffer_delay(bh);
		break;
		return 0;
	case IOMAP_UNWRITTEN:
		/*
		 * For unwritten regions, we always need to ensure that regions
@@ -2062,7 +2063,10 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
		bh->b_blocknr = (iomap->addr + offset - iomap->offset) >>
				inode->i_blkbits;
		set_buffer_mapped(bh);
		break;
		return 0;
	default:
		WARN_ON_ONCE(1);
		return -EIO;
	}
}

@@ -2103,13 +2107,12 @@ int __block_write_begin_int(struct folio *folio, loff_t pos, unsigned len,
			clear_buffer_new(bh);
		if (!buffer_mapped(bh)) {
			WARN_ON(bh->b_size != blocksize);
			if (get_block) {
			if (get_block)
				err = get_block(inode, block, bh, 1);
			else
				err = iomap_to_bh(inode, block, bh, iomap);
			if (err)
				break;
			} else {
				iomap_to_bh(inode, block, bh, iomap);
			}

			if (buffer_new(bh)) {
				clean_bdev_bh_alias(bh);
+18 −12
Original line number Diff line number Diff line
@@ -640,11 +640,13 @@ static int __iomap_write_begin(const struct iomap_iter *iter, loff_t pos,
	size_t poff, plen;

	/*
	 * If the write completely overlaps the current folio, then
	 * If the write or zeroing completely overlaps the current folio, then
	 * entire folio will be dirtied so there is no need for
	 * per-block state tracking structures to be attached to this folio.
	 * For the unshare case, we must read in the ondisk contents because we
	 * are not changing pagecache contents.
	 */
	if (pos <= folio_pos(folio) &&
	if (!(iter->flags & IOMAP_UNSHARE) && pos <= folio_pos(folio) &&
	    pos + len >= folio_pos(folio) + folio_size(folio))
		return 0;

@@ -1261,7 +1263,6 @@ static loff_t iomap_unshare_iter(struct iomap_iter *iter)
	const struct iomap *srcmap = iomap_iter_srcmap(iter);
	loff_t pos = iter->pos;
	loff_t length = iomap_length(iter);
	long status = 0;
	loff_t written = 0;

	/* don't bother with blocks that are not shared to start with */
@@ -1272,28 +1273,33 @@ static loff_t iomap_unshare_iter(struct iomap_iter *iter)
		return length;

	do {
		unsigned long offset = offset_in_page(pos);
		unsigned long bytes = min_t(loff_t, PAGE_SIZE - offset, length);
		struct folio *folio;
		int status;
		size_t offset;
		size_t bytes = min_t(u64, SIZE_MAX, length);

		status = iomap_write_begin(iter, pos, bytes, &folio);
		if (unlikely(status))
			return status;
		if (iter->iomap.flags & IOMAP_F_STALE)
		if (iomap->flags & IOMAP_F_STALE)
			break;

		status = iomap_write_end(iter, pos, bytes, bytes, folio);
		if (WARN_ON_ONCE(status == 0))
		offset = offset_in_folio(folio, pos);
		if (bytes > folio_size(folio) - offset)
			bytes = folio_size(folio) - offset;

		bytes = iomap_write_end(iter, pos, bytes, bytes, folio);
		if (WARN_ON_ONCE(bytes == 0))
			return -EIO;

		cond_resched();

		pos += status;
		written += status;
		length -= status;
		pos += bytes;
		written += bytes;
		length -= bytes;

		balance_dirty_pages_ratelimited(iter->inode->i_mapping);
	} while (length);
	} while (length > 0);

	return written;
}