Skip to content

Commit 26e5750

Browse files
committed
Merge tag 'for-6.2-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba: - fix potential out-of-bounds access to leaf data when seeking in an inline file - fix potential crash in quota when rescan races with disable - reimplement super block signature scratching by marking page/folio dirty and syncing block device, allow removing write_one_page * tag 'for-6.2-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux: btrfs: fix race between quota rescan and disable leading to NULL pointer deref btrfs: fix invalid leaf access due to inline extent during lseek btrfs: stop using write_one_page in btrfs_scratch_superblock btrfs: factor out scratching of one regular super block
2 parents d9166cb + b7adbf9 commit 26e5750

File tree

3 files changed

+52
-36
lines changed

3 files changed

+52
-36
lines changed

fs/btrfs/file.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3541,6 +3541,7 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
35413541
struct extent_buffer *leaf = path->nodes[0];
35423542
struct btrfs_file_extent_item *extent;
35433543
u64 extent_end;
3544+
u8 type;
35443545

35453546
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
35463547
ret = btrfs_next_leaf(root, path);
@@ -3596,10 +3597,16 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
35963597

35973598
extent = btrfs_item_ptr(leaf, path->slots[0],
35983599
struct btrfs_file_extent_item);
3600+
type = btrfs_file_extent_type(leaf, extent);
35993601

3600-
if (btrfs_file_extent_disk_bytenr(leaf, extent) == 0 ||
3601-
btrfs_file_extent_type(leaf, extent) ==
3602-
BTRFS_FILE_EXTENT_PREALLOC) {
3602+
/*
3603+
* Can't access the extent's disk_bytenr field if this is an
3604+
* inline extent, since at that offset, it's where the extent
3605+
* data starts.
3606+
*/
3607+
if (type == BTRFS_FILE_EXTENT_PREALLOC ||
3608+
(type == BTRFS_FILE_EXTENT_REG &&
3609+
btrfs_file_extent_disk_bytenr(leaf, extent) == 0)) {
36033610
/*
36043611
* Explicit hole or prealloc extent, search for delalloc.
36053612
* A prealloc extent is treated like a hole.

fs/btrfs/qgroup.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3367,6 +3367,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
33673367
int err = -ENOMEM;
33683368
int ret = 0;
33693369
bool stopped = false;
3370+
bool did_leaf_rescans = false;
33703371

33713372
path = btrfs_alloc_path();
33723373
if (!path)
@@ -3387,6 +3388,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
33873388
}
33883389

33893390
err = qgroup_rescan_leaf(trans, path);
3391+
did_leaf_rescans = true;
33903392

33913393
if (err > 0)
33923394
btrfs_commit_transaction(trans);
@@ -3407,16 +3409,23 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
34073409
mutex_unlock(&fs_info->qgroup_rescan_lock);
34083410

34093411
/*
3410-
* only update status, since the previous part has already updated the
3411-
* qgroup info.
3412+
* Only update status, since the previous part has already updated the
3413+
* qgroup info, and only if we did any actual work. This also prevents
3414+
* race with a concurrent quota disable, which has already set
3415+
* fs_info->quota_root to NULL and cleared BTRFS_FS_QUOTA_ENABLED at
3416+
* btrfs_quota_disable().
34123417
*/
3413-
trans = btrfs_start_transaction(fs_info->quota_root, 1);
3414-
if (IS_ERR(trans)) {
3415-
err = PTR_ERR(trans);
3418+
if (did_leaf_rescans) {
3419+
trans = btrfs_start_transaction(fs_info->quota_root, 1);
3420+
if (IS_ERR(trans)) {
3421+
err = PTR_ERR(trans);
3422+
trans = NULL;
3423+
btrfs_err(fs_info,
3424+
"fail to start transaction for status update: %d",
3425+
err);
3426+
}
3427+
} else {
34163428
trans = NULL;
3417-
btrfs_err(fs_info,
3418-
"fail to start transaction for status update: %d",
3419-
err);
34203429
}
34213430

34223431
mutex_lock(&fs_info->qgroup_rescan_lock);

fs/btrfs/volumes.c

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,42 +2014,42 @@ static u64 btrfs_num_devices(struct btrfs_fs_info *fs_info)
20142014
return num_devices;
20152015
}
20162016

2017+
static void btrfs_scratch_superblock(struct btrfs_fs_info *fs_info,
2018+
struct block_device *bdev, int copy_num)
2019+
{
2020+
struct btrfs_super_block *disk_super;
2021+
const size_t len = sizeof(disk_super->magic);
2022+
const u64 bytenr = btrfs_sb_offset(copy_num);
2023+
int ret;
2024+
2025+
disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr);
2026+
if (IS_ERR(disk_super))
2027+
return;
2028+
2029+
memset(&disk_super->magic, 0, len);
2030+
folio_mark_dirty(virt_to_folio(disk_super));
2031+
btrfs_release_disk_super(disk_super);
2032+
2033+
ret = sync_blockdev_range(bdev, bytenr, bytenr + len - 1);
2034+
if (ret)
2035+
btrfs_warn(fs_info, "error clearing superblock number %d (%d)",
2036+
copy_num, ret);
2037+
}
2038+
20172039
void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
20182040
struct block_device *bdev,
20192041
const char *device_path)
20202042
{
2021-
struct btrfs_super_block *disk_super;
20222043
int copy_num;
20232044

20242045
if (!bdev)
20252046
return;
20262047

20272048
for (copy_num = 0; copy_num < BTRFS_SUPER_MIRROR_MAX; copy_num++) {
2028-
struct page *page;
2029-
int ret;
2030-
2031-
disk_super = btrfs_read_dev_one_super(bdev, copy_num, false);
2032-
if (IS_ERR(disk_super))
2033-
continue;
2034-
2035-
if (bdev_is_zoned(bdev)) {
2049+
if (bdev_is_zoned(bdev))
20362050
btrfs_reset_sb_log_zones(bdev, copy_num);
2037-
continue;
2038-
}
2039-
2040-
memset(&disk_super->magic, 0, sizeof(disk_super->magic));
2041-
2042-
page = virt_to_page(disk_super);
2043-
set_page_dirty(page);
2044-
lock_page(page);
2045-
/* write_on_page() unlocks the page */
2046-
ret = write_one_page(page);
2047-
if (ret)
2048-
btrfs_warn(fs_info,
2049-
"error clearing superblock number %d (%d)",
2050-
copy_num, ret);
2051-
btrfs_release_disk_super(disk_super);
2052-
2051+
else
2052+
btrfs_scratch_superblock(fs_info, bdev, copy_num);
20532053
}
20542054

20552055
/* Notify udev that device has changed */

0 commit comments

Comments
 (0)