You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
5.1 KiB
221 lines
5.1 KiB
/** |
|
* 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" |
|
|
|
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(le16_to_cpu(inode->i.i_mode)) || |
|
S_ISLNK(le16_to_cpu(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; |
|
}
|
|
|