{
struct btrfs_root *root;
- root = kzalloc(sizeof(*root), flags);
+ root = kzalloc_obj(*root, flags);
if (!root)
return NULL;
}
}
- btrfs_zoned_reserve_data_reloc_bg(fs_info);
btrfs_free_zone_cache(fs_info);
btrfs_check_active_zone_reservation(fs_info);
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;
end, page_ops);
}
-static bool btrfs_verify_folio(struct folio *folio, u64 start, u32 len)
+static bool btrfs_verify_folio(struct fsverity_info *vi, struct folio *folio,
+ u64 start, u32 len)
{
struct btrfs_fs_info *fs_info = folio_to_fs_info(folio);
- if (!fsverity_active(folio->mapping->host) ||
- btrfs_folio_test_uptodate(fs_info, folio, start, len) ||
- start >= i_size_read(folio->mapping->host))
+ if (!vi || btrfs_folio_test_uptodate(fs_info, folio, start, len))
return true;
- return fsverity_verify_folio(folio);
+ return fsverity_verify_folio(vi, folio);
}
-static void end_folio_read(struct folio *folio, bool uptodate, u64 start, u32 len)
+static void end_folio_read(struct fsverity_info *vi, struct folio *folio,
+ bool uptodate, u64 start, u32 len)
{
struct btrfs_fs_info *fs_info = folio_to_fs_info(folio);
ASSERT(folio_pos(folio) <= start &&
start + len <= folio_next_pos(folio));
- if (uptodate && btrfs_verify_folio(folio, start, len))
+ if (uptodate && btrfs_verify_folio(vi, folio, start, len))
btrfs_folio_set_uptodate(fs_info, folio, start, len);
else
btrfs_folio_clear_uptodate(fs_info, folio, start, len);
static void end_bbio_data_read(struct btrfs_bio *bbio)
{
struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
+ struct inode *inode = &bbio->inode->vfs_inode;
struct bio *bio = &bbio->bio;
+ struct fsverity_info *vi = NULL;
struct folio_iter fi;
ASSERT(!bio_flagged(bio, BIO_CLONED));
+
+ if (bbio->file_offset < i_size_read(inode))
+ vi = fsverity_get_info(inode);
+
bio_for_each_folio_all(fi, &bbio->bio) {
bool uptodate = !bio->bi_status;
struct folio *folio = fi.folio;
- struct inode *inode = folio->mapping->host;
u64 start = folio_pos(folio) + fi.offset;
btrfs_debug(fs_info,
}
/* Update page status and unlock. */
- end_folio_read(folio, uptodate, start, fi.length);
+ end_folio_read(vi, folio, uptodate, start, fi.length);
}
bio_put(bio);
}
* return 0 on success, otherwise return error
*/
static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached,
- struct btrfs_bio_ctrl *bio_ctrl)
+ struct btrfs_bio_ctrl *bio_ctrl,
+ struct fsverity_info *vi)
{
struct inode *inode = folio->mapping->host;
struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
ASSERT(IS_ALIGNED(cur, fs_info->sectorsize));
if (cur >= last_byte) {
folio_zero_range(folio, pg_offset, end - cur + 1);
- end_folio_read(folio, true, cur, end - cur + 1);
+ end_folio_read(vi, folio, true, cur, end - cur + 1);
break;
}
if (btrfs_folio_test_uptodate(fs_info, folio, cur, blocksize)) {
- end_folio_read(folio, true, cur, blocksize);
+ end_folio_read(vi, folio, true, cur, blocksize);
continue;
}
/*
*/
em = get_extent_map(BTRFS_I(inode), folio, cur, locked_end - cur + 1, em_cached);
if (IS_ERR(em)) {
- end_folio_read(folio, false, cur, end + 1 - cur);
+ end_folio_read(vi, folio, false, cur, end + 1 - cur);
return PTR_ERR(em);
}
extent_offset = cur - em->start;
/* we've found a hole, just zero and go on */
if (block_start == EXTENT_MAP_HOLE) {
folio_zero_range(folio, pg_offset, blocksize);
- end_folio_read(folio, true, cur, blocksize);
+ end_folio_read(vi, folio, true, cur, blocksize);
continue;
}
/* the get_extent function already copied into the folio */
if (block_start == EXTENT_MAP_INLINE) {
- end_folio_read(folio, true, cur, blocksize);
+ end_folio_read(vi, folio, true, cur, blocksize);
continue;
}
int btrfs_read_folio(struct file *file, struct folio *folio)
{
- struct btrfs_inode *inode = folio_to_inode(folio);
+ struct inode *vfs_inode = folio->mapping->host;
+ struct btrfs_inode *inode = BTRFS_I(vfs_inode);
const u64 start = folio_pos(folio);
const u64 end = start + folio_size(folio) - 1;
struct extent_state *cached_state = NULL;
.last_em_start = U64_MAX,
};
struct extent_map *em_cached = NULL;
+ struct fsverity_info *vi = NULL;
int ret;
lock_extents_for_read(inode, start, end, &cached_state);
- ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl);
+ if (folio_pos(folio) < i_size_read(vfs_inode))
+ vi = fsverity_get_info(vfs_inode);
+ ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi);
btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state);
btrfs_free_extent_map(em_cached);
.last_em_start = U64_MAX,
};
struct folio *folio;
- struct btrfs_inode *inode = BTRFS_I(rac->mapping->host);
+ struct inode *vfs_inode = rac->mapping->host;
+ struct btrfs_inode *inode = BTRFS_I(vfs_inode);
const u64 start = readahead_pos(rac);
const u64 end = start + readahead_length(rac) - 1;
struct extent_state *cached_state = NULL;
struct extent_map *em_cached = NULL;
+ struct fsverity_info *vi = NULL;
lock_extents_for_read(inode, start, end, &cached_state);
-
+ if (start < i_size_read(vfs_inode))
+ vi = fsverity_get_info(vfs_inode);
while ((folio = readahead_folio(rac)) != NULL)
- btrfs_do_readpage(folio, &em_cached, &bio_ctrl);
+ btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi);
btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state);
*/
if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
spin_unlock(&eb->refs_lock);
+ rcu_read_lock();
break;
}
#include <linux/bio.h>
#include <linux/blk-cgroup.h>
#include <linux/file.h>
+#include <linux/filelock.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/pagemap.h>
#include <linux/sched/mm.h>
#include <linux/iomap.h>
#include <linux/unaligned.h>
-#include <linux/fsverity.h>
#include "misc.h"
#include "ctree.h"
#include "disk-io.h"
{
struct async_extent *async_extent;
- async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS);
+ async_extent = kmalloc_obj(*async_extent, GFP_NOFS);
if (!async_extent)
return -ENOMEM;
ASSERT(ram_size < U32_MAX);
const blk_opf_t write_flags = wbc_to_write_flags(wbc);
nofs_flag = memalloc_nofs_save();
- ctx = kvmalloc(struct_size(ctx, chunks, num_chunks), GFP_KERNEL);
+ ctx = kvmalloc_flex(*ctx, chunks, num_chunks);
memalloc_nofs_restore(nofs_flag);
if (!ctx)
return false;
if (folio_test_checked(folio))
return -EAGAIN;
- fixup = kzalloc(sizeof(*fixup), GFP_NOFS);
+ fixup = kzalloc_obj(*fixup, GFP_NOFS);
if (!fixup)
return -EAGAIN;
if (btrfs_is_free_space_inode(inode))
return 0;
- inode->file_extent_tree = kmalloc(sizeof(struct extent_io_tree), GFP_KERNEL);
+ inode->file_extent_tree = kmalloc_obj(struct extent_io_tree);
if (!inode->file_extent_tree)
return -ENOMEM;
trace_btrfs_inode_evict(inode);
- if (!root) {
- fsverity_cleanup_inode(inode);
- clear_inode(inode);
- return;
- }
+ if (!root)
+ goto clear_inode;
fs_info = inode_to_fs_info(inode);
evict_inode_truncate_pages(inode);
* to retry these periodically in the future.
*/
btrfs_remove_delayed_node(BTRFS_I(inode));
- fsverity_cleanup_inode(inode);
+clear_inode:
clear_inode(inode);
}
if (ret)
return ret;
- private = kzalloc(sizeof(struct btrfs_file_private), GFP_KERNEL);
+ private = kzalloc_obj(struct btrfs_file_private);
if (!private)
return -ENOMEM;
private->last_index = last_index;
* We need our own ->update_time so that we can return error on ENOSPC for
* updating the inode in the case of file write and mmap writes.
*/
-static int btrfs_update_time(struct inode *inode, int flags)
+static int btrfs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
- bool dirty;
+ int dirty;
if (btrfs_root_readonly(root))
return -EROFS;
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
- dirty = inode_update_timestamps(inode, flags);
- return dirty ? btrfs_dirty_inode(BTRFS_I(inode)) : 0;
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ return btrfs_dirty_inode(BTRFS_I(inode));
}
/*
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;
struct btrfs_inode *ei = foo;
inode_init_once(&ei->vfs_inode);
-#ifdef CONFIG_FS_VERITY
- ei->i_verity_info = NULL;
-#endif
}
void __cold btrfs_destroy_cachep(void)
{
struct btrfs_delalloc_work *work;
- work = kmalloc(sizeof(*work), GFP_NOFS);
+ work = kmalloc_obj(*work, GFP_NOFS);
if (!work)
return NULL;
* needs longer time span.
*/
if (uring_ctx) {
- priv = kmalloc(sizeof(struct btrfs_encoded_read_private), GFP_NOFS);
+ priv = kmalloc_obj(struct btrfs_encoded_read_private, GFP_NOFS);
if (!priv)
return -ENOMEM;
} else {
ssize_t ret;
nr_pages = DIV_ROUND_UP(disk_io_size, PAGE_SIZE);
- pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
+ pages = kzalloc_objs(struct page *, nr_pages, GFP_NOFS);
if (!pages)
return -ENOMEM;
ret = btrfs_alloc_page_array(nr_pages, pages, false);
struct rb_node **p;
struct rb_node *parent = NULL;
- sp = kmalloc(sizeof(*sp), GFP_NOFS);
+ sp = kmalloc_obj(*sp, GFP_NOFS);
if (!sp)
return -ENOMEM;
sp->ptr = ptr;
#endif
.release = btrfs_release_file,
.fsync = btrfs_sync_file,
+ .setlease = generic_setlease,
};
/*
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;
return -ETXTBSY;
}
- pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL);
+ pending_snapshot = kzalloc_obj(*pending_snapshot);
if (!pending_snapshot)
return -ENOMEM;
ret = get_anon_bdev(&pending_snapshot->anon_dev);
if (ret < 0)
goto free_pending;
- pending_snapshot->root_item = kzalloc(sizeof(struct btrfs_root_item),
- GFP_KERNEL);
+ pending_snapshot->root_item = kzalloc_obj(struct btrfs_root_item);
pending_snapshot->path = btrfs_alloc_path();
if (!pending_snapshot->root_item || !pending_snapshot->path) {
ret = -ENOMEM;
return ret;
}
-/* copy of may_delete in fs/namei.c()
- * Check whether we can remove a link victim from directory dir, check
- * whether the type of victim is right.
- * 1. We can't do it if dir is read-only (done in permission())
- * 2. We should have write and exec permissions on dir
- * 3. We can't remove anything from append-only dir
- * 4. We can't do anything with immutable dir (done in permission())
- * 5. If the sticky bit on dir is set we should either
- * a. be owner of dir, or
- * b. be owner of victim, or
- * c. have CAP_FOWNER capability
- * 6. If the victim is append-only or immutable we can't do anything with
- * links pointing to it.
- * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
- * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
- * 9. We can't remove a root or mountpoint.
- * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
- * nfs_async_unlink().
- */
-
-static int btrfs_may_delete(struct mnt_idmap *idmap,
- struct inode *dir, struct dentry *victim, int isdir)
-{
- int ret;
-
- if (d_really_is_negative(victim))
- return -ENOENT;
-
- /* The @victim is not inside @dir. */
- if (d_inode(victim->d_parent) != dir)
- return -EINVAL;
- audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
-
- ret = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
- if (ret)
- return ret;
- if (IS_APPEND(dir))
- return -EPERM;
- if (check_sticky(idmap, dir, d_inode(victim)) ||
- IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) ||
- IS_SWAPFILE(d_inode(victim)))
- return -EPERM;
- if (isdir) {
- if (!d_is_dir(victim))
- return -ENOTDIR;
- if (IS_ROOT(victim))
- return -EBUSY;
- } else if (d_is_dir(victim))
- return -EISDIR;
- if (IS_DEADDIR(dir))
- return -ENOENT;
- if (victim->d_flags & DCACHE_NFSFS_RENAMED)
- return -EBUSY;
- return 0;
-}
-
-/* copy of may_create in fs/namei.c() */
-static inline int btrfs_may_create(struct mnt_idmap *idmap,
- struct inode *dir, const struct dentry *child)
-{
- if (d_really_is_positive(child))
- return -EEXIST;
- if (IS_DEADDIR(dir))
- return -ENOENT;
- if (!fsuidgid_has_mapping(dir->i_sb, idmap))
- return -EOVERFLOW;
- return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
-}
-
/*
* Create a new subvolume below @parent. This is largely modeled after
* sys_mkdirat and vfs_mkdir, but we only do a single component lookup
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- ret = btrfs_may_create(idmap, dir, dentry);
+ ret = may_create_dentry(idmap, dir, dentry);
if (ret)
goto out_dput;
if (!path)
return -ENOMEM;
- subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+ subvol_info = kzalloc_obj(*subvol_info);
if (!subvol_info) {
btrfs_free_path(path);
return -ENOMEM;
}
/* check if subvolume may be deleted by a user */
- ret = btrfs_may_delete(idmap, dir, dentry, 1);
+ ret = may_delete_dentry(idmap, dir, dentry, true);
if (ret)
goto out_end_removing;
goto out_unlock;
}
- bctl = kzalloc(sizeof(*bctl), GFP_KERNEL);
+ bctl = kzalloc_obj(*bctl);
if (!bctl) {
ret = -ENOMEM;
goto out_unlock;
}
if (sa->assign) {
- prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
+ prealloc = kzalloc_obj(*prealloc);
if (!prealloc) {
ret = -ENOMEM;
goto out;
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)
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;
}
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;
}
if (IS_ERR(args32))
return PTR_ERR(args32);
- args64 = kmalloc(sizeof(*args64), GFP_KERNEL);
+ args64 = kmalloc_obj(*args64);
if (!args64) {
ret = -ENOMEM;
goto out;
ret = copy_from_user(&args32, argp, sizeof(args32));
if (ret)
return -EFAULT;
- arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+ arg = kzalloc_obj(*arg);
if (!arg)
return -ENOMEM;
arg->send_fd = args32.send_fd;
int ret;
nr_pages = DIV_ROUND_UP(disk_io_size, PAGE_SIZE);
- pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
+ pages = kzalloc_objs(struct page *, nr_pages, GFP_NOFS);
if (!pages)
return -ENOMEM;
ret = btrfs_alloc_page_array(nr_pages, pages, 0);
goto out_fail;
}
- priv = kmalloc(sizeof(*priv), GFP_NOFS);
+ priv = kmalloc_obj(*priv, GFP_NOFS);
if (!priv) {
ret = -ENOMEM;
goto out_fail;
}
if (!data) {
- data = kzalloc(sizeof(*data), GFP_NOFS);
+ data = kzalloc_obj(*data, GFP_NOFS);
if (!data) {
ret = -ENOMEM;
goto out_acct;
}
if (!data) {
- data = kzalloc(sizeof(*data), GFP_NOFS);
+ data = kzalloc_obj(*data, GFP_NOFS);
if (!data) {
ret = -ENOMEM;
goto out_acct;
struct mapping_node *node;
struct reloc_control *rc = fs_info->reloc_ctl;
- node = kmalloc(sizeof(*node), GFP_NOFS);
+ node = kmalloc_obj(*node, GFP_NOFS);
if (!node)
return -ENOMEM;
BUG_ON(level == -1);
- block = kmalloc(sizeof(*block), GFP_NOFS);
+ block = kmalloc_obj(*block, GFP_NOFS);
if (!block)
return -ENOMEM;
{
struct reloc_control *rc;
- rc = kzalloc(sizeof(*rc), GFP_NOFS);
+ rc = kzalloc_obj(*rc, GFP_NOFS);
if (!rc)
return NULL;
struct reloc_io_private priv;
unsigned int nr_pages = DIV_ROUND_UP(length, PAGE_SIZE);
- pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
+ pages = kzalloc_objs(struct page *, nr_pages, GFP_NOFS);
if (!pages)
return -ENOMEM;
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);
"parent->subgroup_id=%d", parent->subgroup_id);
ASSERT(id != BTRFS_SUB_GROUP_PRIMARY, "id=%d", id);
- sub_group = kzalloc(sizeof(*sub_group), GFP_NOFS);
+ sub_group = kzalloc_obj(*sub_group, GFP_NOFS);
if (!sub_group)
return -ENOMEM;
struct btrfs_space_info *space_info;
int ret = 0;
- space_info = kzalloc(sizeof(*space_info), GFP_NOFS);
+ space_info = kzalloc_obj(*space_info, GFP_NOFS);
if (!space_info)
return -ENOMEM;
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);
+ }
}
}
}
*/
BUG_ON(type == TRANS_JOIN_NOLOCK);
- cur_trans = kmalloc(sizeof(*cur_trans), GFP_NOFS);
+ cur_trans = kmalloc_obj(*cur_trans, GFP_NOFS);
if (!cur_trans)
return -ENOMEM;
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;