| /** |
| * 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; |
| |
| 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); |
| reserve_new_block(sbi, &blkaddr, &sum, type); |
| |
| /* 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, unsigned 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; |
| } |
| |
| void 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], 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]; |
| } |