2015-12-10 00:18:44 +00:00
|
|
|
/**
|
|
|
|
* 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"
|
|
|
|
|
2019-08-05 09:44:06 +00:00
|
|
|
void f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
|
2015-12-10 00:18:44 +00:00
|
|
|
{
|
|
|
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
2019-08-05 09:44:06 +00:00
|
|
|
nid_t i;
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
f2fs-tools: fix to skip block allocation for fsynced data
Previously, we don't allow block allocation on unclean umounted image,
result in failing to repair quota system file.
In this patch, we port most recovery codes from kernel to userspace
tools, so that on unclean image, during fsck initialization, we will
record all data/node block address we may recover in kernel, and
then during allocation of quota file repair, we can skip those blocks
to avoid block use conflict.
Eventually, if free space is enough, we can repair the quota system
file on an unclean umounted image.
Signed-off-by: Chao Yu <yuchao0@huawei.com>
[Jaegeuk Kim: remove unnecessary parameter]
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2019-08-14 08:48:55 +00:00
|
|
|
void f2fs_release_nid(struct f2fs_sb_info *sbi, nid_t nid)
|
|
|
|
{
|
|
|
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
|
|
|
|
|
|
|
ASSERT(nid < nm_i->max_nid);
|
|
|
|
ASSERT(f2fs_test_bit(nid, nm_i->nid_bitmap));
|
|
|
|
|
|
|
|
f2fs_clear_bit(nid, nm_i->nid_bitmap);
|
|
|
|
}
|
|
|
|
|
f2fs-tools: rebuild the quota inode if it is corrupted
commit 1228009520d1d2cb392ae52f8aaf3c6ec42edccf
category: bugfix
issue: #I6VAS0
CVE: NA
Signed-off-by: DongSenhao <dongsenhao2@huawei.com>
---------------------------------------
If the following process returns an error,
the quota inode, not the quota file, is damaged.
(fsck_chk_quota_node-->fsck_chk_node_blk-->sanity_check_nid)
The fsck does not have a process to rebuild the quota inode.
Because sanity_check_nid is not passed, fsck->nat_area_bitmap
can not be cleared, and then the NAT of quota will be nullify
during fix_nat_entries.
During the next fsck check, the quota inode check fails
because the address of the quota inode changes to 0.
In addition, in fsck_chk_quota_files-->f2fs_filesize_update,
data is written to address 0.
Therefore, when the quota inode is corrupted, we need to rebuild it.
Signed-off-by: Wang Xiaojun <wangxiaojun11@huawei.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: dongsenhao <dongsenhao2@huawei.com>
2021-07-20 06:41:18 +00:00
|
|
|
int f2fs_rebuild_qf_inode(struct f2fs_sb_info *sbi, int qtype)
|
|
|
|
{
|
|
|
|
struct f2fs_node *raw_node = NULL;
|
|
|
|
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
|
|
|
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
|
|
|
struct f2fs_summary sum;
|
|
|
|
struct node_info ni;
|
|
|
|
nid_t ino = QUOTA_INO(sb, qtype);
|
|
|
|
block_t blkaddr = NULL_ADDR;
|
|
|
|
__u64 cp_ver = cur_cp_version(ckpt);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
raw_node = calloc(F2FS_BLKSIZE, 1);
|
|
|
|
if (raw_node == NULL) {
|
|
|
|
MSG(1, "\tError: Calloc Failed for raw_node!!!\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
f2fs_init_qf_inode(sb, raw_node, qtype, time(NULL));
|
|
|
|
|
|
|
|
if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG))
|
|
|
|
cp_ver |= (cur_cp_crc(ckpt) << 32);
|
|
|
|
raw_node->footer.cp_ver = cpu_to_le64(cp_ver);
|
|
|
|
|
|
|
|
get_node_info(sbi, ino, &ni);
|
|
|
|
set_summary(&sum, ino, 0, ni.version);
|
|
|
|
ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE, 1);
|
|
|
|
if (ret) {
|
|
|
|
MSG(1, "\tError: Failed to reserve new block!\n");
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = write_inode(raw_node, blkaddr);
|
|
|
|
if (ret < 0) {
|
|
|
|
MSG(1, "\tError: While rebuilding the quota inode to disk!\n");
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
update_nat_blkaddr(sbi, ino, ino, blkaddr);
|
|
|
|
|
|
|
|
f2fs_clear_bit(ino, F2FS_FSCK(sbi)->nat_area_bitmap);
|
|
|
|
f2fs_set_bit(ino, NM_I(sbi)->nid_bitmap);
|
|
|
|
DBG(1, "Rebuild quota inode ([%3d] ino [0x%x]) at offset:0x%x\n",
|
|
|
|
qtype, ino, blkaddr);
|
|
|
|
err_out:
|
|
|
|
free(raw_node);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-12-10 00:18:44 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-06-30 21:03:51 +00:00
|
|
|
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
2015-12-10 00:18:44 +00:00
|
|
|
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;
|
2017-10-30 23:21:09 +00:00
|
|
|
block_t blkaddr = NULL_ADDR;
|
2015-12-10 00:18:44 +00:00
|
|
|
int type;
|
2019-04-15 09:14:38 +00:00
|
|
|
int ret;
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
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;
|
2020-06-18 12:48:07 +00:00
|
|
|
set_cold_node(node_blk, S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)));
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
type = CURSEG_COLD_NODE;
|
|
|
|
if (IS_DNODE(node_blk)) {
|
2017-01-19 03:03:40 +00:00
|
|
|
if (S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)))
|
2015-12-10 00:18:44 +00:00
|
|
|
type = CURSEG_HOT_NODE;
|
|
|
|
else
|
|
|
|
type = CURSEG_WARM_NODE;
|
|
|
|
}
|
|
|
|
|
2020-06-30 21:03:51 +00:00
|
|
|
if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
|
|
|
|
type != CURSEG_HOT_NODE)
|
|
|
|
type = CURSEG_HOT_NODE;
|
|
|
|
|
2015-12-10 00:18:44 +00:00
|
|
|
get_node_info(sbi, dn->nid, &ni);
|
|
|
|
set_summary(&sum, dn->nid, 0, ni.version);
|
2019-08-05 09:44:06 +00:00
|
|
|
ret = reserve_new_block(sbi, &blkaddr, &sum, type, !ofs);
|
2019-04-15 09:14:38 +00:00
|
|
|
if (ret) {
|
|
|
|
free(node_blk);
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
*/
|
2017-11-02 17:41:16 +00:00
|
|
|
static int get_node_path(struct f2fs_node *node, long block,
|
2015-12-10 00:18:44 +00:00
|
|
|
int offset[4], unsigned int noffset[4])
|
|
|
|
{
|
2017-07-26 14:49:57 +00:00
|
|
|
const long direct_index = ADDRS_PER_INODE(&node->i);
|
2019-03-25 13:19:35 +00:00
|
|
|
const long direct_blks = ADDRS_PER_BLOCK(&node->i);
|
2015-12-10 00:18:44 +00:00
|
|
|
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
2019-03-25 13:19:35 +00:00
|
|
|
const long indirect_blks = ADDRS_PER_BLOCK(&node->i) * NIDS_PER_BLOCK;
|
2015-12-10 00:18:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-10-01 01:16:38 +00:00
|
|
|
int get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
|
2015-12-10 00:18:44 +00:00
|
|
|
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;
|
|
|
|
|
2017-07-26 14:49:57 +00:00
|
|
|
level = get_node_path(dn->inode_blk, index, offset, noffset);
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
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) {
|
2019-08-05 09:44:06 +00:00
|
|
|
f2fs_alloc_nid(sbi, &nids[i]);
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
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]);
|
f2fs-tools: fix to skip block allocation for fsynced data
Previously, we don't allow block allocation on unclean umounted image,
result in failing to repair quota system file.
In this patch, we port most recovery codes from kernel to userspace
tools, so that on unclean image, during fsck initialization, we will
record all data/node block address we may recover in kernel, and
then during allocation of quota file repair, we can skip those blocks
to avoid block use conflict.
Eventually, if free space is enough, we can repair the quota system
file on an unclean umounted image.
Signed-off-by: Chao Yu <yuchao0@huawei.com>
[Jaegeuk Kim: remove unnecessary parameter]
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2019-08-14 08:48:55 +00:00
|
|
|
if (!nblk[i]) {
|
|
|
|
f2fs_release_nid(sbi, nids[i]);
|
|
|
|
c.alloc_failed = 1;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-12-10 00:18:44 +00:00
|
|
|
|
|
|
|
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];
|
2018-10-01 01:16:38 +00:00
|
|
|
return 0;
|
2015-12-10 00:18:44 +00:00
|
|
|
}
|