third_party_f2fs-tools/fsck/node.c
Chao Yu 7d4480baa9 f2fs-tools: fix to check total valid block count before block allocation
Now, kernel can catch incorrect total valid block count which is exceed
max user block count of image.

Then, generic/051,476 of fstest reports below message:

Apr 15 11:08:03 szvp000201624 kernel: [ 2533.515813] F2FS-fs (zram1): Wrong valid_user_blocks: 469505, user_block_count: 469504
Apr 15 11:08:03 szvp000201624 kernel: [ 2533.519166] F2FS-fs (zram1): Failed to get valid F2FS checkpoint

The reason is that when fsck repairs corrupted quota sysfile, it didn't
check max user block count when allocating new block for quota sysfile,
so ckpt.valid_block_count can exceed max user block count, result in
mount failure later.

Adding upper boundary check of block count in reserve_new_block() to
fix this issue.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2019-08-20 11:23:51 -07:00

263 lines
6.3 KiB
C

/**
* node.c
*
* Many parts of codes are copied from Linux kernel/fs/f2fs.
*
* Copyright (C) 2015 Huawei Ltd.
* Witten by:
* Hou Pengyang <houpengyang@huawei.com>
* Liu Shuoran <liushuoran@huawei.com>
* Jaegeuk Kim <jaegeuk@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "fsck.h"
#include "node.h"
void f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid, int inode)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
nid_t i, inode_cnt, node_cnt;
for (i = 0; i < nm_i->max_nid; i++)
if(f2fs_test_bit(i, nm_i->nid_bitmap) == 0)
break;
ASSERT(i < nm_i->max_nid);
f2fs_set_bit(i, nm_i->nid_bitmap);
*nid = i;
inode_cnt = get_cp(valid_inode_count);
node_cnt = get_cp(valid_node_count);
if (inode)
set_cp(valid_inode_count, inode_cnt + 1);
set_cp(valid_node_count, node_cnt + 1);
}
void set_data_blkaddr(struct dnode_of_data *dn)
{
__le32 *addr_array;
struct f2fs_node *node_blk = dn->node_blk;
unsigned int ofs_in_node = dn->ofs_in_node;
addr_array = blkaddr_in_node(node_blk);
addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
if (dn->node_blk != dn->inode_blk)
dn->ndirty = 1;
else
dn->idirty = 1;
}
/*
* In this function, we get a new node blk, and write back
* node_blk would be sloadd in RAM, linked by dn->node_blk
*/
block_t new_node_block(struct f2fs_sb_info *sbi,
struct dnode_of_data *dn, unsigned int ofs)
{
struct f2fs_node *f2fs_inode;
struct f2fs_node *node_blk;
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
struct f2fs_summary sum;
struct node_info ni;
block_t blkaddr = NULL_ADDR;
int type;
int ret;
f2fs_inode = dn->inode_blk;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
node_blk->footer.nid = cpu_to_le32(dn->nid);
node_blk->footer.ino = f2fs_inode->footer.ino;
node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
node_blk->footer.cp_ver = ckpt->checkpoint_ver;
type = CURSEG_COLD_NODE;
if (IS_DNODE(node_blk)) {
if (S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)))
type = CURSEG_HOT_NODE;
else
type = CURSEG_WARM_NODE;
}
get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, 0, ni.version);
ret = reserve_new_block(sbi, &blkaddr, &sum, type);
if (ret) {
free(node_blk);
return 0;
}
/* update nat info */
update_nat_blkaddr(sbi, le32_to_cpu(f2fs_inode->footer.ino),
dn->nid, blkaddr);
dn->node_blk = node_blk;
inc_inode_blocks(dn);
return blkaddr;
}
/*
* get_node_path - Get the index path of pgoff_t block
* @offset: offset in the current index node block.
* @noffset: NO. of the index block within a file.
* return: depth of the index path.
*
* By default, it sets inline_xattr and inline_data
*/
static int get_node_path(struct f2fs_node *node, long block,
int offset[4], unsigned int noffset[4])
{
const long direct_index = ADDRS_PER_INODE(&node->i);
const long direct_blks = ADDRS_PER_BLOCK;
const long dptrs_per_blk = NIDS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;
int n = 0;
int level = 0;
noffset[0] = 0;
if (block < direct_index) {
offset[n] = block;
goto got;
}
block -= direct_index;
if (block < direct_blks) {
offset[n++] = NODE_DIR1_BLOCK;
noffset[n]= 1;
offset[n] = block;
level = 1;
goto got;
}
block -= direct_blks;
if (block < direct_blks) {
offset[n++] = NODE_DIR2_BLOCK;
noffset[n] = 2;
offset[n] = block;
level = 1;
goto got;
}
block -= direct_blks;
if (block < indirect_blks) {
offset[n++] = NODE_IND1_BLOCK;
noffset[n] = 3;
offset[n++] = block / direct_blks;
noffset[n] = 4 + offset[n - 1];
offset[n] = block % direct_blks;
level = 2;
goto got;
}
block -= indirect_blks;
if (block < indirect_blks) {
offset[n++] = NODE_IND2_BLOCK;
noffset[n] = 4 + dptrs_per_blk;
offset[n++] = block / direct_blks;
noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
offset[n] = block % direct_blks;
level = 2;
goto got;
}
block -= indirect_blks;
if (block < dindirect_blks) {
offset[n++] = NODE_DIND_BLOCK;
noffset[n] = 5 + (dptrs_per_blk * 2);
offset[n++] = block / indirect_blks;
noffset[n] = 6 + (dptrs_per_blk * 2) +
offset[n - 1] * (dptrs_per_blk + 1);
offset[n++] = (block / direct_blks) % dptrs_per_blk;
noffset[n] = 7 + (dptrs_per_blk * 2) +
offset[n - 2] * (dptrs_per_blk + 1) +
offset[n - 1];
offset[n] = block % direct_blks;
level = 3;
goto got;
} else {
ASSERT(0);
}
got:
return level;
}
int get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
pgoff_t index, int mode)
{
int offset[4];
unsigned int noffset[4];
struct f2fs_node *parent = NULL;
nid_t nids[4];
block_t nblk[4];
struct node_info ni;
int level, i;
int ret;
level = get_node_path(dn->inode_blk, index, offset, noffset);
nids[0] = dn->nid;
parent = dn->inode_blk;
if (level != 0)
nids[1] = get_nid(parent, offset[0], 1);
else
dn->node_blk = dn->inode_blk;
get_node_info(sbi, nids[0], &ni);
nblk[0] = ni.blk_addr;
for (i = 1; i <= level; i++) {
if (!nids[i] && mode == ALLOC_NODE) {
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
if (!is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) {
c.alloc_failed = 1;
return -EINVAL;
}
f2fs_alloc_nid(sbi, &nids[i], 0);
dn->nid = nids[i];
/* Function new_node_blk get a new f2fs_node blk and update*/
/* We should make sure that dn->node_blk == NULL*/
nblk[i] = new_node_block(sbi, dn, noffset[i]);
ASSERT(nblk[i]);
set_nid(parent, offset[i - 1], nids[i], i == 1);
} else {
/* If Sparse file no read API, */
struct node_info ni;
get_node_info(sbi, nids[i], &ni);
dn->node_blk = calloc(BLOCK_SZ, 1);
ASSERT(dn->node_blk);
ret = dev_read_block(dn->node_blk, ni.blk_addr);
ASSERT(ret >= 0);
nblk[i] = ni.blk_addr;
}
if (mode == ALLOC_NODE){
/* Parent node may have changed */
ret = dev_write_block(parent, nblk[i - 1]);
ASSERT(ret >= 0);
}
if (i != 1)
free(parent);
if (i < level) {
parent = dn->node_blk;
nids[i + 1] = get_nid(parent, offset[i], 0);
}
}
dn->nid = nids[level];
dn->ofs_in_node = offset[level];
dn->data_blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
dn->node_blkaddr = nblk[level];
return 0;
}