summaryrefslogtreecommitdiff
path: root/fs/f2fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/f2fs')
-rw-r--r--fs/f2fs/acl.c18
-rw-r--r--fs/f2fs/checkpoint.c14
-rw-r--r--fs/f2fs/data.c49
-rw-r--r--fs/f2fs/extent_cache.c19
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/file.c11
-rw-r--r--fs/f2fs/gc.c56
-rw-r--r--fs/f2fs/inode.c26
-rw-r--r--fs/f2fs/node.c8
9 files changed, 144 insertions, 58 deletions
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index fa8d81a30fb9..d3253549173e 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -47,6 +47,7 @@ static inline int f2fs_acl_count(size_t size)
static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
{
int i, count;
+ int err = -EINVAL;
struct posix_acl *acl;
struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
@@ -70,8 +71,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
for (i = 0; i < count; i++) {
- if ((char *)entry > end)
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry_short) > end)) {
+ err = -EFSCORRUPTED;
goto fail;
+ }
acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag);
acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
@@ -86,6 +90,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
break;
case ACL_USER:
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry) > end)) {
+ err = -EFSCORRUPTED;
+ goto fail;
+ }
acl->a_entries[i].e_uid =
make_kuid(&init_user_ns,
le32_to_cpu(entry->e_id));
@@ -93,6 +102,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
sizeof(struct f2fs_acl_entry));
break;
case ACL_GROUP:
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry) > end)) {
+ err = -EFSCORRUPTED;
+ goto fail;
+ }
acl->a_entries[i].e_gid =
make_kgid(&init_user_ns,
le32_to_cpu(entry->e_id));
@@ -108,7 +122,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
return acl;
fail:
posix_acl_release(acl);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(err);
}
static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index c00a6b6ebcbd..064f5b537423 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -943,6 +943,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
for (i = 0; i < orphan_blocks; i++) {
struct folio *folio;
struct f2fs_orphan_block *orphan_blk;
+ unsigned int entry_count;
folio = f2fs_get_meta_folio(sbi, start_blk + i);
if (IS_ERR(folio)) {
@@ -951,7 +952,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
}
orphan_blk = folio_address(folio);
- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
+ entry_count = le32_to_cpu(orphan_blk->entry_count);
+ if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
+ f2fs_err(sbi, "invalid orphan inode entry count %u",
+ entry_count);
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN);
+ err = -EFSCORRUPTED;
+ f2fs_folio_put(folio, true);
+ goto out;
+ }
+
+ for (j = 0; j < entry_count; j++) {
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
err = recover_orphan_inode(sbi, ino);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 8d4f1e75dee3..e24b74859427 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2495,7 +2495,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
unsigned nrpages;
struct f2fs_folio_state *ffs;
int ret = 0;
- bool folio_in_bio;
+ bool folio_in_bio = false;
if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) {
if (folio)
@@ -2611,18 +2611,17 @@ submit_and_realloc:
}
trace_f2fs_read_folio(folio, DATA);
err_out:
- if (!folio_in_bio) {
+ if (!folio_in_bio)
folio_end_read(folio, !ret);
- if (ret)
- return ret;
- }
+ if (ret)
+ goto out;
if (rac) {
folio = readahead_folio(rac);
goto next_folio;
}
out:
f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
- if (ret) {
+ if (ret && folio_in_bio) {
/* Wait bios and clear uptodate. */
folio_lock(folio);
folio_clear_uptodate(folio);
@@ -3822,13 +3821,14 @@ unlock_out:
static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
struct folio *folio, loff_t pos, unsigned int len,
- block_t *blk_addr, bool *node_changed, bool *use_cow)
+ block_t *blk_addr, bool *node_changed)
{
struct inode *inode = folio->mapping->host;
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
pgoff_t index = folio->index;
int err = 0;
block_t ori_blk_addr = NULL_ADDR;
+ bool cow_has_reserved_block = false;
/* If pos is beyond the end of file, reserve a new block in COW inode */
if ((pos & PAGE_MASK) >= i_size_read(inode))
@@ -3836,12 +3836,14 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
/* Look for the block in COW inode first */
err = __find_data_block(cow_inode, index, blk_addr);
- if (err) {
+ if (err)
return err;
- } else if (*blk_addr != NULL_ADDR) {
- *use_cow = true;
+
+ if (__is_valid_data_blkaddr(*blk_addr))
return 0;
- }
+
+ if (*blk_addr == NEW_ADDR)
+ cow_has_reserved_block = true;
if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE))
goto reserve_block;
@@ -3853,10 +3855,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
reserve_block:
/* Finally, we should reserve a new block in COW inode for the update */
- err = __reserve_data_block(cow_inode, index, blk_addr, node_changed);
- if (err)
- return err;
- inc_atomic_write_cnt(inode);
+ if (!cow_has_reserved_block) {
+ err = __reserve_data_block(cow_inode, index, blk_addr,
+ node_changed);
+ if (err)
+ return err;
+ inc_atomic_write_cnt(inode);
+ }
if (ori_blk_addr != NULL_ADDR)
*blk_addr = ori_blk_addr;
@@ -3873,7 +3878,6 @@ static int f2fs_write_begin(const struct kiocb *iocb,
struct folio *folio;
pgoff_t index = pos >> PAGE_SHIFT;
bool need_balance = false;
- bool use_cow = false;
block_t blkaddr = NULL_ADDR;
int err = 0;
@@ -3936,7 +3940,7 @@ repeat:
if (f2fs_is_atomic_file(inode))
err = prepare_atomic_write_begin(sbi, folio, pos, len,
- &blkaddr, &need_balance, &use_cow);
+ &blkaddr, &need_balance);
else
err = prepare_write_begin(sbi, folio, pos, len,
&blkaddr, &need_balance);
@@ -3976,8 +3980,15 @@ repeat:
err = -EFSCORRUPTED;
goto put_folio;
}
- f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode :
- inode,
+ /*
+ * Although the block may be stored in the COW inode, the folio
+ * belongs to @inode and its data was encrypted (or not) using
+ * @inode's context (see f2fs_encrypt_one_page()). Read with
+ * @inode so the post-read decryption decision matches the
+ * folio's owner; otherwise an unencrypted @inode whose COW inode
+ * is encrypted hits a NULL ->i_crypt_info on decryption.
+ */
+ f2fs_submit_page_read(inode,
NULL, /* can't write to fsverity files */
folio, blkaddr, 0, true);
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index d2e006420f04..61f6b9714366 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)
if (!__init_may_extent_tree(inode, type))
return false;
- if (is_inode_flag_set(inode, FI_NO_EXTENT))
- return false;
-
if (type == EX_READ) {
+ if (is_inode_flag_set(inode, FI_NO_EXTENT))
+ return false;
if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
!f2fs_sb_has_readonly(F2FS_I_SB(inode)))
return false;
@@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode,
while (atomic_read(&et->node_cnt)) {
write_lock(&et->lock);
- if (!is_inode_flag_set(inode, FI_NO_EXTENT))
- set_inode_flag(inode, FI_NO_EXTENT);
node_cnt += __free_extent_tree(sbi, et, nr_shrink);
write_unlock(&et->lock);
}
- f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
-
return node_cnt;
}
@@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode,
write_lock(&et->lock);
- if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
- write_unlock(&et->lock);
- return;
- }
-
if (type == EX_READ) {
+ if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
+ write_unlock(&et->lock);
+ return;
+ }
+
prev = et->largest;
dei.len = 0;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 91f506e7c9cf..069595fc5e1a 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1583,6 +1583,7 @@ enum node_type {
NODE_TYPE_INODE,
NODE_TYPE_XATTR,
NODE_TYPE_NON_INODE,
+ NODE_TYPE_NON_IXNODE, /* non inode and xnode */
};
/* a threshold of maximum elapsed time in critical region to print tracepoint */
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index fb12c5c9affd..ade4fde2ef0e 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1098,6 +1098,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
return -EPERM;
if ((attr->ia_valid & ATTR_SIZE)) {
+ if (mapping_large_folio_support(inode->i_mapping))
+ return -EOPNOTSUPP;
if (!f2fs_is_compress_backend_ready(inode) ||
IS_DEVICE_ALIASING(inode))
return -EOPNOTSUPP;
@@ -1914,8 +1916,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
if (f2fs_is_pinned_file(inode)) {
block_t sec_blks = CAP_BLKS_PER_SEC(sbi);
- block_t sec_len = roundup(map.m_len, sec_blks);
+ block_t sec_len;
+ if (map.m_lblk % sec_blks) {
+ map.m_lblk = rounddown(map.m_lblk, sec_blks);
+ map.m_len = pg_end - map.m_lblk;
+ if (off_end)
+ map.m_len++;
+ }
+ sec_len = roundup(map.m_len, sec_blks);
map.m_len = sec_blks;
next_alloc:
f2fs_down_write(&sbi->pin_sem);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index ba93010924c0..11228349b003 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static int ra_data_block(struct inode *inode, pgoff_t index)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct dnode_of_data dn;
struct folio *folio, *efolio;
struct f2fs_io_info fio = {
@@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
};
int err = 0;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
folio = f2fs_grab_cache_folio(mapping, index, true);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (f2fs_lookup_read_extent_cache_block(inode, index,
&dn.data_blkaddr)) {
@@ -1299,11 +1312,16 @@ got_it:
f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
+ if (atomic_inode)
+ iput(atomic_inode);
return 0;
put_encrypted_page:
f2fs_put_page(fio.encrypted_page, true);
put_folio:
f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
@@ -1314,8 +1332,8 @@ put_folio:
static int move_data_block(struct inode *inode, block_t bidx,
int gc_type, unsigned int segno, int off)
{
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode),
.ino = inode->i_ino,
@@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx,
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
/* do not read out */
folio = f2fs_grab_cache_folio(mapping, bidx, false);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
err = -ENOENT;
@@ -1468,11 +1499,10 @@ up_out:
put_out:
f2fs_put_dnode(&dn);
out:
- if (!folio_test_uptodate(folio))
- __folio_set_dropbehind(folio);
- folio_unlock(folio);
- folio_end_dropbehind(folio);
- folio_put(folio);
+ f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index c6dcda447882..5f0cf81e49f0 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -324,9 +324,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio)
}
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
- f2fs_has_inline_xattr(inode) &&
- (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE ||
- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
+ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
+ (f2fs_has_inline_xattr(inode) &&
+ fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu",
__func__, inode->i_ino, fi->i_inline_xattr_size,
MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
@@ -561,8 +561,13 @@ static int do_read_inode(struct inode *inode)
static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino)
{
- return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) ||
- ino == F2FS_COMPRESS_INO(sbi);
+ if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
+ return true;
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+ if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi))
+ return true;
+#endif
+ return false;
}
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
@@ -858,10 +863,15 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_abort_atomic_write(inode, true);
if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
- clear_inode_flag(fi->cow_inode, FI_COW_FILE);
- F2FS_I(fi->cow_inode)->atomic_inode = NULL;
- iput(fi->cow_inode);
+ struct inode *cow_inode = fi->cow_inode;
+
+ f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
+ clear_inode_flag(cow_inode, FI_COW_FILE);
+ F2FS_I(cow_inode)->atomic_inode = NULL;
fi->cow_inode = NULL;
+ f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
+
+ iput(cow_inode);
}
trace_f2fs_evict_inode(inode);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 4e5bd9e4cfc3..01d860e7e169 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1541,6 +1541,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
if (is_inode)
goto out_err;
break;
+ case NODE_TYPE_NON_IXNODE:
+ if (is_inode || is_xnode)
+ goto out_err;
+ break;
default:
break;
}
@@ -1634,7 +1638,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start)
struct f2fs_sb_info *sbi = F2FS_F_SB(parent);
nid_t nid = get_nid(parent, start, false);
- return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR);
+ return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE);
}
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
@@ -1875,7 +1879,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
}
if (!__write_node_folio(node_folio, false, false, NULL,
- &wbc, false, FS_GC_NODE_IO, NULL))
+ &wbc, false, io_type, NULL))
err = -EAGAIN;
goto release_folio;
out_folio: