| /** |
| * segment.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" |
| |
| int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, |
| struct f2fs_summary *sum, int type, bool is_inode) |
| { |
| struct f2fs_fsck *fsck = F2FS_FSCK(sbi); |
| struct seg_entry *se; |
| u64 blkaddr, offset; |
| u64 old_blkaddr = *to; |
| bool is_node = IS_NODESEG(type); |
| |
| if (old_blkaddr == NULL_ADDR) { |
| if (c.func == FSCK) { |
| if (fsck->chk.valid_blk_cnt >= sbi->user_block_count) { |
| ERR_MSG("Not enough space\n"); |
| return -ENOSPC; |
| } |
| if (is_node && fsck->chk.valid_node_cnt >= |
| sbi->total_valid_node_count) { |
| ERR_MSG("Not enough space for node block\n"); |
| return -ENOSPC; |
| } |
| } else { |
| if (sbi->total_valid_block_count >= |
| sbi->user_block_count) { |
| ERR_MSG("Not enough space\n"); |
| return -ENOSPC; |
| } |
| if (is_node && sbi->total_valid_node_count >= |
| sbi->total_node_count) { |
| ERR_MSG("Not enough space for node block\n"); |
| return -ENOSPC; |
| } |
| } |
| } |
| |
| blkaddr = SM_I(sbi)->main_blkaddr; |
| |
| if (find_next_free_block(sbi, &blkaddr, 0, type, false)) { |
| ERR_MSG("Can't find free block"); |
| 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); |
| if (need_fsync_data_record(sbi)) { |
| se->ckpt_type = type; |
| se->ckpt_valid_blocks++; |
| f2fs_set_bit(offset, (char *)se->ckpt_valid_map); |
| } |
| if (c.func == FSCK) { |
| f2fs_set_main_bitmap(sbi, blkaddr, type); |
| f2fs_set_sit_bitmap(sbi, blkaddr); |
| } |
| |
| if (old_blkaddr == NULL_ADDR) { |
| sbi->total_valid_block_count++; |
| if (is_node) { |
| sbi->total_valid_node_count++; |
| if (is_inode) |
| sbi->total_valid_inode_count++; |
| } |
| if (c.func == FSCK) { |
| fsck->chk.valid_blk_cnt++; |
| if (is_node) { |
| fsck->chk.valid_node_cnt++; |
| if (is_inode) |
| fsck->chk.valid_inode_cnt++; |
| } |
| } |
| } |
| se->dirty = 1; |
| |
| /* read/write SSA */ |
| *to = (block_t)blkaddr; |
| update_sum_entry(sbi, *to, sum); |
| |
| return 0; |
| } |
| |
| int 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; |
| unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node); |
| int ret; |
| |
| 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); |
| ret = reserve_new_block(sbi, &dn->data_blkaddr, &sum, type, 0); |
| if (ret) { |
| c.alloc_failed = 1; |
| return ret; |
| } |
| |
| if (blkaddr == NULL_ADDR) |
| inc_inode_blocks(dn); |
| else if (blkaddr == NEW_ADDR) |
| dn->idirty = 1; |
| set_data_blkaddr(dn); |
| return 0; |
| } |
| |
| u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer, |
| u64 count, pgoff_t offset) |
| { |
| struct dnode_of_data dn; |
| struct node_info ni; |
| struct f2fs_node *inode; |
| char *blk_buffer; |
| u64 filesize; |
| u64 off_in_blk; |
| u64 len_in_blk; |
| u64 read_count; |
| u64 remained_blkentries; |
| block_t blkaddr; |
| void *index_node = NULL; |
| |
| memset(&dn, 0, sizeof(dn)); |
| |
| /* Memory allocation for block buffer and inode. */ |
| blk_buffer = calloc(BLOCK_SZ, 2); |
| ASSERT(blk_buffer); |
| inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ); |
| |
| /* Read inode */ |
| get_node_info(sbi, ino, &ni); |
| ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); |
| ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); |
| ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); |
| |
| /* Adjust count with file length. */ |
| filesize = le64_to_cpu(inode->i.i_size); |
| if (offset > filesize) |
| count = 0; |
| else if (count + offset > filesize) |
| count = filesize - offset; |
| |
| /* Main loop for file blocks */ |
| read_count = remained_blkentries = 0; |
| while (count > 0) { |
| if (remained_blkentries == 0) { |
| set_new_dnode(&dn, inode, NULL, ino); |
| get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset), |
| LOOKUP_NODE); |
| if (index_node) |
| free(index_node); |
| index_node = (dn.node_blk == dn.inode_blk) ? |
| NULL : dn.node_blk; |
| remained_blkentries = ADDRS_PER_PAGE(sbi, |
| dn.node_blk, dn.inode_blk); |
| } |
| ASSERT(remained_blkentries > 0); |
| |
| blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); |
| if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) |
| break; |
| |
| off_in_blk = offset % BLOCK_SZ; |
| len_in_blk = BLOCK_SZ - off_in_blk; |
| if (len_in_blk > count) |
| len_in_blk = count; |
| |
| /* Read data from single block. */ |
| if (len_in_blk < BLOCK_SZ) { |
| ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0); |
| memcpy(buffer, blk_buffer + off_in_blk, len_in_blk); |
| } else { |
| /* Direct read */ |
| ASSERT(dev_read_block(buffer, blkaddr) >= 0); |
| } |
| |
| offset += len_in_blk; |
| count -= len_in_blk; |
| buffer += len_in_blk; |
| read_count += len_in_blk; |
| |
| dn.ofs_in_node++; |
| remained_blkentries--; |
| } |
| if (index_node) |
| free(index_node); |
| free(blk_buffer); |
| |
| return read_count; |
| } |
| |
| u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer, |
| u64 count, pgoff_t offset) |
| { |
| struct dnode_of_data dn; |
| struct node_info ni; |
| struct f2fs_node *inode; |
| char *blk_buffer; |
| u64 off_in_blk; |
| u64 len_in_blk; |
| u64 written_count; |
| u64 remained_blkentries; |
| block_t blkaddr; |
| void* index_node = NULL; |
| int idirty = 0; |
| int err; |
| |
| /* Memory allocation for block buffer and inode. */ |
| blk_buffer = calloc(BLOCK_SZ, 2); |
| ASSERT(blk_buffer); |
| inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ); |
| |
| /* Read inode */ |
| get_node_info(sbi, ino, &ni); |
| ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); |
| ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); |
| ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); |
| |
| /* Main loop for file blocks */ |
| written_count = remained_blkentries = 0; |
| while (count > 0) { |
| if (remained_blkentries == 0) { |
| set_new_dnode(&dn, inode, NULL, ino); |
| err = get_dnode_of_data(sbi, &dn, |
| F2FS_BYTES_TO_BLK(offset), ALLOC_NODE); |
| if (err) |
| break; |
| idirty |= dn.idirty; |
| if (index_node) |
| free(index_node); |
| index_node = (dn.node_blk == dn.inode_blk) ? |
| NULL : dn.node_blk; |
| remained_blkentries = ADDRS_PER_PAGE(sbi, |
| dn.node_blk, dn.inode_blk); |
| } |
| ASSERT(remained_blkentries > 0); |
| |
| blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); |
| if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { |
| err = new_data_block(sbi, blk_buffer, |
| &dn, CURSEG_WARM_DATA); |
| if (err) |
| break; |
| blkaddr = dn.data_blkaddr; |
| } |
| |
| off_in_blk = offset % BLOCK_SZ; |
| len_in_blk = BLOCK_SZ - off_in_blk; |
| if (len_in_blk > count) |
| len_in_blk = count; |
| |
| /* Write data to single block. */ |
| if (len_in_blk < BLOCK_SZ) { |
| ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0); |
| memcpy(blk_buffer + off_in_blk, buffer, len_in_blk); |
| ASSERT(dev_write_block(blk_buffer, blkaddr) >= 0); |
| } else { |
| /* Direct write */ |
| ASSERT(dev_write_block(buffer, blkaddr) >= 0); |
| } |
| |
| offset += len_in_blk; |
| count -= len_in_blk; |
| buffer += len_in_blk; |
| written_count += len_in_blk; |
| |
| dn.ofs_in_node++; |
| if ((--remained_blkentries == 0 || count == 0) && (dn.ndirty)) |
| ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr) >= 0); |
| } |
| if (offset > le64_to_cpu(inode->i.i_size)) { |
| inode->i.i_size = cpu_to_le64(offset); |
| idirty = 1; |
| } |
| if (idirty) { |
| ASSERT(inode == dn.inode_blk); |
| ASSERT(write_inode(inode, ni.blk_addr) >= 0); |
| } |
| if (index_node) |
| free(index_node); |
| free(blk_buffer); |
| |
| return written_count; |
| } |
| |
| /* This function updates only inode->i.i_size */ |
| void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize) |
| { |
| struct node_info ni; |
| struct f2fs_node *inode; |
| |
| inode = calloc(BLOCK_SZ, 1); |
| ASSERT(inode); |
| get_node_info(sbi, ino, &ni); |
| |
| ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); |
| ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); |
| ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); |
| |
| inode->i.i_size = cpu_to_le64(filesize); |
| |
| ASSERT(write_inode(inode, ni.blk_addr) >= 0); |
| free(inode); |
| } |
| |
| int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de) |
| { |
| int fd, n; |
| pgoff_t off = 0; |
| u8 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 <= DEF_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; |
| |
| if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { |
| node_blk->i.i_inline |= F2FS_EXTRA_ATTR; |
| node_blk->i.i_extra_isize = |
| cpu_to_le16(calc_extra_isize()); |
| } |
| n = read(fd, buffer, BLOCK_SZ); |
| ASSERT((unsigned long)n == de->size); |
| memcpy(inline_data_addr(node_blk), buffer, de->size); |
| node_blk->i.i_size = cpu_to_le64(de->size); |
| ASSERT(write_inode(node_blk, ni.blk_addr) >= 0); |
| free(node_blk); |
| } else { |
| while ((n = read(fd, buffer, BLOCK_SZ)) > 0) { |
| f2fs_write(sbi, de->ino, buffer, n, off); |
| off += n; |
| } |
| } |
| |
| close(fd); |
| if (n < 0) |
| return -1; |
| |
| update_free_segments(sbi); |
| |
| MSG(1, "Info: Create %s -> %s\n" |
| " -- ino=%x, type=%x, mode=%x, uid=%x, " |
| "gid=%x, cap=%"PRIx64", size=%lu, pino=%x\n", |
| de->full_path, de->path, |
| de->ino, de->file_type, de->mode, |
| de->uid, de->gid, de->capabilities, de->size, de->pino); |
| return 0; |
| } |