blob: c3e383b7d13a1f1e1ca99a5c0ca0062341f2c5a3 [file] [log] [blame]
/**
* 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)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
nid_t i;
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;
}
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);
}
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;
}
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_super_block *sb = F2FS_RAW_SUPER(sbi);
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;
set_cold_node(node_blk, S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)));
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;
}
if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
type != CURSEG_HOT_NODE)
type = CURSEG_HOT_NODE;
get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, 0, ni.version);
ret = reserve_new_block(sbi, &blkaddr, &sum, type, !ofs);
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(&node->i);
const long dptrs_per_blk = NIDS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK(&node->i) * 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) {
f2fs_alloc_nid(sbi, &nids[i]);
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]);
if (!nblk[i]) {
f2fs_release_nid(sbi, nids[i]);
c.alloc_failed = 1;
return -EINVAL;
}
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;
}