for-7.0-rc3-tag

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAmmytygACgkQxWXV+ddt
 WDuiKw/+IyoS2IE95Jd4G2hezcsaqJNAvv5q90vVyLepT/7jzRbYrHytk7z/0v56
 +Pjc1JHgp+TidYKZa52E/R09eEvYCyuZvEq4bnClOnO3CAJDCixqTKWV70CcYiHX
 HoSCuPML2CJMLZY3u6MxATgk46y1ey3UkbmQ4oufoUHrjAE+D3pDQsYQ9BWR/P6J
 4LbyZ214uIUPvbp0wPJ14cVUMpNxs116JmWvK9dxWQNZSzFcq2IVuLwQUcZBPAdb
 dursd0kt6HXXmNCITmUD8O+ipG4U1akn8FuCjaLqo4LF1AH2f6OzNjucsisfa1tE
 4MD+ATzmNsew5q6dtyfcvv8Btl+olbTP4KGibLl9PCCM9vlkzl3EN5GPUllGi6S9
 T8jqe2pILXZHEx1DIQjHaXJsnuHG9aEkUbkSHUxIa15QDj66omrZZkZG3EF5Buy9
 TogJJgESYU19dt/y110Q/vD/LPOgJhB3NBXwIx1FDYA1OwaAjt6hUcVRVRI5PtpL
 moAIG4nNrPz5Pa875NvguFmrXFIudpTqyANHCPVio4eqDR7LeSg9vVHj9zeOfeRD
 gmn3WGuGNb6L+86TNet/nFQhjxhkKqsnSbzO2zoPQKhG6FS+HSLnUwvJYL4/YnkW
 QEeveeI/6Duiei4DHuw7FXmZVNQxeBEWW6MTFDHA1UNV3HMKewA=
 =TgSD
 -----END PGP SIGNATURE-----

Merge tag 'for-7.0-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:

 - detect possible file name hash collision earlier so it does not lead
   to transaction abort

 - handle b-tree leaf overflows when snapshotting a subvolume with set
   received UUID, leading to transaction abort

 - in zoned mode, reorder relocation block group initialization after
   the transaction kthread start

 - fix orphan cleanup state tracking of subvolume, this could lead to
   invalid dentries under some conditions

 - add locking around updates of dynamic reclain state update

 - in subpage mode, add missing RCU unlock when trying to releae extent
   buffer

 - remap tree fixes:
     - add missing description strings for the newly added remap tree
     - properly update search key when iterating backrefs

* tag 'for-7.0-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: remove duplicated definition of btrfs_printk_in_rcu()
  btrfs: remove unnecessary transaction abort in the received subvol ioctl
  btrfs: abort transaction on failure to update root in the received subvol ioctl
  btrfs: fix transaction abort on set received ioctl due to item overflow
  btrfs: fix transaction abort when snapshotting received subvolumes
  btrfs: fix transaction abort on file creation due to name hash collision
  btrfs: read key again after incrementing slot in move_existing_remaps()
  btrfs: add missing RCU unlock in error path in try_release_subpage_extent_buffer()
  btrfs: set BTRFS_ROOT_ORPHAN_CLEANUP during subvol create
  btrfs: zoned: move btrfs_zoned_reserve_data_reloc_bg() after kthread start
  btrfs: hold space_info->lock when clearing periodic reclaim ready
  btrfs: print-tree: add remap tree definitions
This commit is contained in:
Linus Torvalds 2026-03-12 12:15:27 -07:00
commit e0b38d286e
11 changed files with 126 additions and 9 deletions

View File

@ -3594,7 +3594,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
}
}
btrfs_zoned_reserve_data_reloc_bg(fs_info);
btrfs_free_zone_cache(fs_info);
btrfs_check_active_zone_reservation(fs_info);
@ -3622,6 +3621,12 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
goto fail_cleaner;
}
/*
* Starts a transaction, must be called after the transaction kthread
* is initialized.
*/
btrfs_zoned_reserve_data_reloc_bg(fs_info);
ret = btrfs_read_qgroup_config(fs_info);
if (ret)
goto fail_trans_kthread;

View File

@ -4507,6 +4507,7 @@ static int try_release_subpage_extent_buffer(struct folio *folio)
*/
if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
spin_unlock(&eb->refs_lock);
rcu_read_lock();
break;
}

View File

@ -6612,6 +6612,25 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
int ret;
bool xa_reserved = false;
if (!args->orphan && !args->subvol) {
/*
* Before anything else, check if we can add the name to the
* parent directory. We want to avoid a dir item overflow in
* case we have an existing dir item due to existing name
* hash collisions. We do this check here before we call
* btrfs_add_link() down below so that we can avoid a
* transaction abort (which could be exploited by malicious
* users).
*
* For subvolumes we already do this in btrfs_mksubvol().
*/
ret = btrfs_check_dir_item_collision(BTRFS_I(dir)->root,
btrfs_ino(BTRFS_I(dir)),
name);
if (ret < 0)
return ret;
}
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;

View File

@ -672,6 +672,13 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
goto out;
}
/*
* Subvolumes have orphans cleaned on first dentry lookup. A new
* subvolume cannot have any orphans, so we should set the bit before we
* add the subvolume dentry to the dentry cache, so that it is in the
* same state as a subvolume after first lookup.
*/
set_bit(BTRFS_ROOT_ORPHAN_CLEANUP, &new_root->state);
d_instantiate_new(dentry, new_inode_args.inode);
new_inode_args.inode = NULL;
@ -3852,6 +3859,25 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
goto out;
}
received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid,
BTRFS_UUID_SIZE);
/*
* Before we attempt to add the new received uuid, check if we have room
* for it in case there's already an item. If the size of the existing
* item plus this root's ID (u64) exceeds the maximum item size, we can
* return here without the need to abort a transaction. If we don't do
* this check, the btrfs_uuid_tree_add() call below would fail with
* -EOVERFLOW and result in a transaction abort. Malicious users could
* exploit this to turn the fs into RO mode.
*/
if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) {
ret = btrfs_uuid_tree_check_overflow(fs_info, sa->uuid,
BTRFS_UUID_KEY_RECEIVED_SUBVOL);
if (ret < 0)
goto out;
}
/*
* 1 - root item
* 2 - uuid items (received uuid + subvol uuid)
@ -3867,15 +3893,12 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
sa->rtime.sec = ct.tv_sec;
sa->rtime.nsec = ct.tv_nsec;
received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid,
BTRFS_UUID_SIZE);
if (received_uuid_changed &&
!btrfs_is_empty_uuid(root_item->received_uuid)) {
ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid,
BTRFS_UUID_KEY_RECEIVED_SUBVOL,
btrfs_root_id(root));
if (unlikely(ret && ret != -ENOENT)) {
btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans);
goto out;
}
@ -3890,7 +3913,8 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
ret = btrfs_update_root(trans, fs_info->tree_root,
&root->root_key, &root->root_item);
if (ret < 0) {
if (unlikely(ret < 0)) {
btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans);
goto out;
}

View File

@ -28,9 +28,6 @@ void _btrfs_printk(const struct btrfs_fs_info *fs_info, unsigned int level, cons
#else
#define btrfs_printk_in_rcu(fs_info, level, fmt, args...) \
btrfs_no_printk(fs_info, fmt, ##args)
#define btrfs_printk_in_rcu(fs_info, level, fmt, args...) \
btrfs_no_printk(fs_info, fmt, ##args)

View File

@ -38,6 +38,7 @@ static const struct root_name_map root_map[] = {
{ BTRFS_BLOCK_GROUP_TREE_OBJECTID, "BLOCK_GROUP_TREE" },
{ BTRFS_DATA_RELOC_TREE_OBJECTID, "DATA_RELOC_TREE" },
{ BTRFS_RAID_STRIPE_TREE_OBJECTID, "RAID_STRIPE_TREE" },
{ BTRFS_REMAP_TREE_OBJECTID, "REMAP_TREE" },
};
const char *btrfs_root_name(const struct btrfs_key *key, char *buf)
@ -415,6 +416,9 @@ static void key_type_string(const struct btrfs_key *key, char *buf, int buf_size
[BTRFS_UUID_KEY_SUBVOL] = "UUID_KEY_SUBVOL",
[BTRFS_UUID_KEY_RECEIVED_SUBVOL] = "UUID_KEY_RECEIVED_SUBVOL",
[BTRFS_RAID_STRIPE_KEY] = "RAID_STRIPE",
[BTRFS_IDENTITY_REMAP_KEY] = "IDENTITY_REMAP",
[BTRFS_REMAP_KEY] = "REMAP",
[BTRFS_REMAP_BACKREF_KEY] = "REMAP_BACKREF",
};
if (key->type == 0 && key->objectid == BTRFS_FREE_SPACE_OBJECTID)
@ -435,6 +439,7 @@ void btrfs_print_leaf(const struct extent_buffer *l)
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_dev_extent *dev_extent;
struct btrfs_remap_item *remap;
struct btrfs_key key;
if (!l)
@ -569,6 +574,11 @@ void btrfs_print_leaf(const struct extent_buffer *l)
print_raid_stripe_key(l, btrfs_item_size(l, i),
btrfs_item_ptr(l, i, struct btrfs_stripe_extent));
break;
case BTRFS_REMAP_KEY:
case BTRFS_REMAP_BACKREF_KEY:
remap = btrfs_item_ptr(l, i, struct btrfs_remap_item);
pr_info("\t\taddress %llu\n", btrfs_remap_address(l, remap));
break;
}
}
}

View File

@ -4399,6 +4399,8 @@ static int move_existing_remaps(struct btrfs_fs_info *fs_info,
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
}
remap = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_remap_item);

View File

@ -2194,8 +2194,11 @@ void btrfs_reclaim_sweep(const struct btrfs_fs_info *fs_info)
if (!btrfs_should_periodic_reclaim(space_info))
continue;
for (raid = 0; raid < BTRFS_NR_RAID_TYPES; raid++) {
if (do_reclaim_sweep(space_info, raid))
if (do_reclaim_sweep(space_info, raid)) {
spin_lock(&space_info->lock);
btrfs_set_periodic_reclaim_ready(space_info, false);
spin_unlock(&space_info->lock);
}
}
}
}

View File

@ -1905,6 +1905,22 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
ret = btrfs_uuid_tree_add(trans, new_root_item->received_uuid,
BTRFS_UUID_KEY_RECEIVED_SUBVOL,
objectid);
/*
* We are creating of lot of snapshots of the same root that was
* received (has a received UUID) and reached a leaf's limit for
* an item. We can safely ignore this and avoid a transaction
* abort. A deletion of this snapshot will still work since we
* ignore if an item with a BTRFS_UUID_KEY_RECEIVED_SUBVOL key
* is missing (see btrfs_delete_subvolume()). Send/receive will
* work too since it peeks the first root id from the existing
* item (it could peek any), and in case it's missing it
* falls back to search by BTRFS_UUID_KEY_SUBVOL keys.
* Creation of a snapshot does not require CAP_SYS_ADMIN, so
* we don't want users triggering transaction aborts, either
* intentionally or not.
*/
if (ret == -EOVERFLOW)
ret = 0;
if (unlikely(ret && ret != -EEXIST)) {
btrfs_abort_transaction(trans, ret);
goto fail;

View File

@ -199,6 +199,44 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
return 0;
}
/*
* Check if we can add one root ID to a UUID key.
* If the key does not yet exists, we can, otherwise only if extended item does
* not exceeds the maximum item size permitted by the leaf size.
*
* Returns 0 on success, negative value on error.
*/
int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info,
const u8 *uuid, u8 type)
{
BTRFS_PATH_AUTO_FREE(path);
int ret;
u32 item_size;
struct btrfs_key key;
if (WARN_ON_ONCE(!fs_info->uuid_root))
return -EINVAL;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
btrfs_uuid_to_key(uuid, type, &key);
ret = btrfs_search_slot(NULL, fs_info->uuid_root, &key, path, 0, 0);
if (ret < 0)
return ret;
if (ret > 0)
return 0;
item_size = btrfs_item_size(path->nodes[0], path->slots[0]);
if (sizeof(struct btrfs_item) + item_size + sizeof(u64) >
BTRFS_LEAF_DATA_SIZE(fs_info))
return -EOVERFLOW;
return 0;
}
static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
u64 subid)
{

View File

@ -12,6 +12,8 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 typ
u64 subid);
int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8 type,
u64 subid);
int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info,
const u8 *uuid, u8 type);
int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info);
int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info);
int btrfs_uuid_scan_kthread(void *data);