/** * segment.c * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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 reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, struct f2fs_summary *sum, int type) { struct seg_entry *se; u64 blkaddr; u64 offset; blkaddr = SM_I(sbi)->main_blkaddr; if (find_next_free_block(sbi, &blkaddr, 0, type)) { ERR_MSG("Not enough space to allocate blocks"); ASSERT(0); } se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr)); offset = OFFSET_IN_SEG(sbi, blkaddr); se->type = type; se->valid_blocks++; f2fs_set_bit(offset, (char *)se->cur_valid_map); sbi->total_valid_block_count++; se->dirty = 1; /* read/write SSA */ *to = (block_t)blkaddr; update_sum_entry(sbi, *to, sum); } void new_data_block(struct f2fs_sb_info *sbi, void *block, struct dnode_of_data *dn, int type) { struct f2fs_summary sum; struct node_info ni; ASSERT(dn->node_blk); memset(block, 0, BLOCK_SZ); get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); reserve_new_block(sbi, &dn->data_blkaddr, &sum, type); inc_inode_blocks(dn); set_data_blkaddr(dn); } static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer, u64 count, pgoff_t offset) { u64 start = F2FS_BYTES_TO_BLK(offset); u64 len = F2FS_BYTES_TO_BLK(count); u64 end_offset; u64 off_in_block, len_in_block, len_already; struct dnode_of_data dn = {0}; void *data_blk; struct node_info ni; struct f2fs_node *inode; int ret = -1; get_node_info(sbi, ino, &ni); inode = calloc(BLOCK_SZ, 1); ASSERT(inode); ret = dev_read_block(inode, ni.blk_addr); ASSERT(ret >= 0); if (S_ISDIR(inode->i.i_mode) || S_ISLNK(inode->i.i_mode)) ASSERT(0); off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1); len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block; len_already = 0; /* * When calculate how many blocks this 'count' stride accross, * We should take offset in a block in account. */ len = F2FS_BYTES_TO_BLK(count + off_in_block + ((1 << F2FS_BLKSIZE_BITS) - 1)); data_blk = calloc(BLOCK_SZ, 1); ASSERT(data_blk); set_new_dnode(&dn, inode, NULL, ino); while (len) { if (dn.node_blk != dn.inode_blk) free(dn.node_blk); set_new_dnode(&dn, inode, NULL, ino); get_dnode_of_data(sbi, &dn, start, ALLOC_NODE); end_offset = ADDRS_PER_PAGE(dn.node_blk); while (dn.ofs_in_node < end_offset && len) { block_t blkaddr; blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); /* A new page from WARM_DATA */ if (blkaddr == NULL_ADDR) new_data_block(sbi, data_blk, &dn, CURSEG_WARM_DATA); /* Copy data from buffer to file */ ret = dev_read_block(data_blk, dn.data_blkaddr); ASSERT(ret >= 0); memcpy(data_blk + off_in_block, buffer, len_in_block); ret = dev_write_block(data_blk, dn.data_blkaddr); ASSERT(ret >= 0); off_in_block = 0; len_already += len_in_block; if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS)) len_in_block = 1 << F2FS_BLKSIZE_BITS; else len_in_block = count - len_already; len--; start++; dn.ofs_in_node++; } /* Update the direct node */ if (dn.ndirty) { ret = dev_write_block(dn.node_blk, dn.node_blkaddr); ASSERT(ret >= 0); } } /* Update the inode info */ if (le64_to_cpu(inode->i.i_size) < offset + count) { inode->i.i_size = cpu_to_le64(offset + count); dn.idirty = 1; } if (dn.idirty) { ASSERT(inode == dn.inode_blk); ret = dev_write_block(inode, ni.blk_addr); ASSERT(ret >= 0); } if (dn.node_blk && dn.node_blk != dn.inode_blk) free(dn.node_blk); free(data_blk); free(inode); } int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de) { int fd, n; pgoff_t off = 0; char buffer[BLOCK_SZ]; if (de->ino == 0) return -1; fd = open(de->full_path, O_RDONLY); if (fd < 0) { MSG(0, "Skip: Fail to open %s\n", de->full_path); return -1; } /* inline_data support */ if (de->size <= MAX_INLINE_DATA) { struct node_info ni; struct f2fs_node *node_blk; int ret; get_node_info(sbi, de->ino, &ni); node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); node_blk->i.i_inline |= F2FS_INLINE_DATA; node_blk->i.i_inline |= F2FS_DATA_EXIST; n = read(fd, buffer, BLOCK_SZ); ASSERT(n == de->size); memcpy(&node_blk->i.i_addr[1], buffer, de->size); node_blk->i.i_size = cpu_to_le64(de->size); ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); free(node_blk); } else { while ((n = read(fd, buffer, BLOCK_SZ)) > 0) { f2fs_write_block(sbi, de->ino, buffer, n, off); off += n; } } close(fd); if (n < 0) return -1; update_free_segments(sbi); MSG(1, "Info: built a file %s, size=%lu\n", de->full_path, de->size); return 0; }