diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2015-12-09 16:18:44 -0800 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-04-18 21:11:26 -0400 |
commit | 603f8f9d3628e8b81fea301e5f543a9f0b0d902d (patch) | |
tree | 903a3a88d22de833be6c852559f74712f079df7d /fsck/dir.c | |
parent | d3be08825e9f5ebe8e9bf9da9a9b77cefd9b525d (diff) | |
download | f2fs-tools-603f8f9d3628e8b81fea301e5f543a9f0b0d902d.tar.gz |
sload.f2fs: support loading files into partition directly
This patch implements loading files into the existing partition.
For example,
# sload.f2fs -f ./ /dev/sdb1
Then, all the directories and files will be loaded into /dev/sdb1.
By default, newly files should have inline_data and inline_xattr, if possible.
Signed-off-by: Hou Pengyang <houpengyang@huawei.com>
Signed-off-by: Liu Shuoran <liushuoran@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fsck/dir.c')
-rw-r--r-- | fsck/dir.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/fsck/dir.c b/fsck/dir.c new file mode 100644 index 0000000..18b79bd --- /dev/null +++ b/fsck/dir.c @@ -0,0 +1,584 @@ +/** + * dir.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" + +static unsigned int dir_buckets(unsigned int level) +{ + if (level < MAX_DIR_HASH_DEPTH / 2) + return 1 << level; + else + return MAX_DIR_BUCKETS; +} + +static unsigned int bucket_blocks(unsigned int level) +{ + if (level < MAX_DIR_HASH_DEPTH / 2) + return 2; + else + return 4; +} + +static unsigned long dir_block_index(unsigned int level, + int dir_level, unsigned int idx) +{ + unsigned long i; + unsigned long bidx = 0; + + for (i = 0; i < level; i++) + bidx += dir_buckets(i + dir_level) * bucket_blocks(i); + bidx += idx * bucket_blocks(level); + return bidx; +} + +static int room_for_filename(const u8 *bitmap, int slots, int max_slots) +{ + int bit_start = 0; + int zero_start, zero_end; +next: + zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start); + if (zero_start >= max_slots) + return max_slots; + + zero_end = find_next_bit_le(bitmap, max_slots, zero_start + 1); + + if (zero_end - zero_start >= slots) + return zero_start; + bit_start = zero_end; + goto next; + +} + +static void make_dentry_ptr(struct f2fs_dentry_ptr *d, void *src, int type) +{ + if (type == 1) { + struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; + d->max = NR_DENTRY_IN_BLOCK; + d->bitmap = t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } else { + struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src; + d->max = NR_INLINE_DENTRY; + d->bitmap = t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } +} + +static struct f2fs_dir_entry *find_target_dentry(const u8 *name, + unsigned int len, f2fs_hash_t namehash, int *max_slots, + struct f2fs_dentry_ptr *d) +{ + struct f2fs_dir_entry *de; + unsigned long bit_pos = 0; + int max_len = 0; + + if (max_slots) + *max_slots = 0; + while (bit_pos < d->max) { + if (!test_bit_le(bit_pos, d->bitmap)) { + bit_pos++; + max_len++; + continue; + } + + de = &d->dentry[bit_pos]; + if (le16_to_cpu(de->name_len) == len && + de->hash_code == namehash && + !memcmp(d->filename[bit_pos], name, len)) { + goto found; + } + + if (max_slots && max_len > *max_slots) + *max_slots = max_len; + max_len = 0; + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + de = NULL; +found: + if (max_slots && max_len > *max_slots) + *max_slots = max_len; + return de; +} + +static struct f2fs_dir_entry *find_in_block(void *block, + const u8 *name, int len, f2fs_hash_t namehash, + int *max_slots) +{ + struct f2fs_dentry_ptr d; + + make_dentry_ptr(&d, block, 1); + return find_target_dentry(name, len, namehash, max_slots, &d); +} + +static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir, + unsigned int level, struct dentry *de) +{ + unsigned int nbucket, nblock; + unsigned int bidx, end_block; + struct f2fs_dir_entry *dentry = NULL; + struct dnode_of_data dn = {0}; + void *dentry_blk; + int max_slots = 214; + nid_t ino = dir->footer.ino; + f2fs_hash_t namehash; + int ret = 0; + + namehash = f2fs_dentry_hash(de->name, de->len); + + nbucket = dir_buckets(level); + nblock = bucket_blocks(level); + + bidx = dir_block_index(level, 0, le32_to_cpu(namehash) % nbucket); + end_block = bidx + nblock; + + dentry_blk = calloc(BLOCK_SZ, 1); + ASSERT(dentry_blk); + + for (; bidx < end_block; bidx++) { + + /* Firstly, we should know direct node of target data blk */ + if (dn.node_blk && dn.node_blk != dn.inode_blk) + free(dn.node_blk); + + set_new_dnode(&dn, dir, NULL, ino); + get_dnode_of_data(sbi, &dn, bidx, LOOKUP_NODE); + if (dn.data_blkaddr == NULL_ADDR) + continue; + + ret = dev_read_block(dentry_blk, dn.data_blkaddr); + ASSERT(ret >= 0); + + dentry = find_in_block(dentry_blk, de->name, de->len, + namehash, &max_slots); + if (dentry) { + ret = 1; + de->ino = le32_to_cpu(dentry->ino); + break; + } + } + + if (dn.node_blk && dn.node_blk != dn.inode_blk) + free(dn.node_blk); + free(dentry_blk); + + return ret; +} + +static int f2fs_find_entry(struct f2fs_sb_info *sbi, + struct f2fs_node *dir, struct dentry *de) +{ + unsigned int max_depth; + unsigned int level; + + max_depth = dir->i.i_current_depth; + for (level = 0; level < max_depth; level ++) { + if (find_in_level(sbi, dir, level, de)) + return 1; + } + return 0; +} + +static void f2fs_update_dentry(nid_t ino, umode_t mode, + struct f2fs_dentry_ptr *d, + const unsigned char *name, int len, f2fs_hash_t name_hash, + unsigned int bit_pos) +{ + struct f2fs_dir_entry *de; + int slots = GET_DENTRY_SLOTS(len); + int i; + + de = &d->dentry[bit_pos]; + de->name_len = len; + de->hash_code = name_hash; + memcpy(d->filename[bit_pos], name, len); + d->filename[bit_pos][len] = 0; + de->ino = cpu_to_le32(ino); + set_de_type(de, mode); + for (i = 0; i < slots; i++) + test_and_set_bit_le(bit_pos + i, d->bitmap); +} + +/* + * f2fs_add_link - Add a new file(dir) to parent dir. + */ +static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent, + struct f2fs_node *child, block_t p_blkaddr) +{ + int level = 0, current_depth, bit_pos; + int nbucket, nblock, bidx, block; + const unsigned char *name = child->i.i_name; + int name_len = le32_to_cpu(child->i.i_namelen); + int slots = GET_DENTRY_SLOTS(name_len); + f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len); + struct f2fs_dentry_block *dentry_blk; + struct f2fs_dentry_ptr d; + struct dnode_of_data dn = {0}; + nid_t pino = le32_to_cpu(parent->footer.ino); + nid_t ino = le32_to_cpu(child->footer.ino); + umode_t mode = le16_to_cpu(child->i.i_mode); + int ret; + + if (parent == NULL || child == NULL) + return -EINVAL; + + if (!pino) { + ERR_MSG("Wrong parent ino:%d \n", pino); + return -EINVAL; + } + + dentry_blk = calloc(BLOCK_SZ, 1); + ASSERT(dentry_blk); + + current_depth = le32_to_cpu(parent->i.i_current_depth); +start: + if (current_depth == MAX_DIR_HASH_DEPTH) { + free(dentry_blk); + ERR_MSG("\tError: MAX_DIR_HASH\n"); + return -ENOSPC; + } + + /* Need a new dentry block */ + if (level == current_depth) + ++current_depth; + + nbucket = dir_buckets(level); + nblock = bucket_blocks(level); + bidx = dir_block_index(level, 0, le32_to_cpu(dentry_hash) % nbucket); + + for (block = bidx; block <= (bidx + nblock - 1); block++) { + + /* Firstly, we should know the direct node of target data blk */ + if (dn.node_blk && dn.node_blk != dn.inode_blk) + free(dn.node_blk); + + set_new_dnode(&dn, parent, NULL, pino); + get_dnode_of_data(sbi, &dn, block, ALLOC_NODE); + + if (dn.data_blkaddr == NULL_ADDR) { + new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA); + } else { + ret = dev_read_block(dentry_blk, dn.data_blkaddr); + ASSERT(ret >= 0); + } + bit_pos = room_for_filename(dentry_blk->dentry_bitmap, + slots, NR_DENTRY_IN_BLOCK); + + if (bit_pos < NR_DENTRY_IN_BLOCK) + goto add_dentry; + } + level ++; + goto start; + +add_dentry: + make_dentry_ptr(&d, (void *)dentry_blk, 1); + f2fs_update_dentry(ino, mode, &d, name, name_len, dentry_hash, bit_pos); + + ret = dev_write_block(dentry_blk, dn.data_blkaddr); + ASSERT(ret >= 0); + + /* + * Parent inode needs updating, because its inode info may be changed. + * such as i_current_depth and i_blocks. + */ + if (parent->i.i_current_depth != cpu_to_le32(current_depth)) { + parent->i.i_current_depth = cpu_to_le32(current_depth); + dn.idirty = 1; + } + + /* Update parent's i_links info*/ + if (S_ISDIR(mode)) { + u32 links = le32_to_cpu(parent->i.i_links); + parent->i.i_links = cpu_to_le32(links + 1); + dn.idirty = 1; + } + + if ((block + 1) * F2FS_BLKSIZE > le64_to_cpu(parent->i.i_size)) { + parent->i.i_size = cpu_to_le64((block + 1) * F2FS_BLKSIZE); + dn.idirty = 1; + } + + if (dn.ndirty) { + ret = dev_write_block(dn.node_blk, dn.node_blkaddr); + ASSERT(ret >= 0); + } + + if (dn.idirty) { + ASSERT(parent == dn.inode_blk); + ret = dev_write_block(dn.inode_blk, p_blkaddr); + ASSERT(ret >= 0); + } + + if (dn.node_blk != dn.inode_blk) + free(dn.node_blk); + free(dentry_blk); + return 0; +} + +static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode) +{ + struct f2fs_dentry_block *dent_blk; + nid_t ino = le32_to_cpu(inode->footer.ino); + nid_t pino = le32_to_cpu(inode->i.i_pino); + struct f2fs_summary sum; + struct node_info ni; + block_t blkaddr; + int ret; + + get_node_info(sbi, ino, &ni); + + dent_blk = calloc(BLOCK_SZ, 1); + ASSERT(dent_blk); + + dent_blk->dentry[0].hash_code = 0; + dent_blk->dentry[0].ino = cpu_to_le32(ino); + dent_blk->dentry[0].name_len = cpu_to_le16(1); + dent_blk->dentry[0].file_type = F2FS_FT_DIR; + memcpy(dent_blk->filename[0], ".", 1); + + dent_blk->dentry[1].hash_code = 0; + dent_blk->dentry[1].ino = cpu_to_le32(pino); + dent_blk->dentry[1].name_len = cpu_to_le16(2); + dent_blk->dentry[1].file_type = F2FS_FT_DIR; + memcpy(dent_blk->filename[1], "..", 2); + + test_and_set_bit_le(0, dent_blk->dentry_bitmap); + test_and_set_bit_le(1, dent_blk->dentry_bitmap); + + set_summary(&sum, ino, 0, ni.version); + reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_DATA); + + ret = dev_write_block(dent_blk, blkaddr); + ASSERT(ret >= 0); + + inode->i.i_addr[0] = cpu_to_le32(blkaddr); + free(dent_blk); +} + +static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode, + const char *symname, int symlen) +{ + nid_t ino = le32_to_cpu(inode->footer.ino); + struct f2fs_summary sum; + struct node_info ni; + char *data_blk; + block_t blkaddr; + int ret; + + get_node_info(sbi, ino, &ni); + + /* store into inline_data */ + if (symlen + 1 <= MAX_INLINE_DATA) { + inode->i.i_inline |= F2FS_INLINE_DATA; + inode->i.i_inline |= F2FS_DATA_EXIST; + memcpy(&inode->i.i_addr[1], symname, symlen); + return; + } + + data_blk = calloc(BLOCK_SZ, 1); + ASSERT(data_blk); + + memcpy(data_blk, symname, symlen); + + set_summary(&sum, ino, 0, ni.version); + reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA); + + ret = dev_write_block(data_blk, blkaddr); + ASSERT(ret >= 0); + + inode->i.i_addr[0] = cpu_to_le32(blkaddr); + free(data_blk); +} + +static void init_inode_block(struct f2fs_sb_info *sbi, + struct f2fs_node *node_blk, struct dentry *de) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + mode_t mode = de->mode; + int links = 1; + unsigned int size; + int blocks = 1; + + if (de->file_type == F2FS_FT_DIR) { + mode |= S_IFDIR; + size = 4096; + links++; + blocks++; + } else if (de->file_type == F2FS_FT_REG_FILE) { + mode |= S_IFREG; + size = 0; + } else if (de->file_type == F2FS_FT_SYMLINK) { + ASSERT(de->link); + mode |= S_IFLNK; + size = strlen(de->link); + if (size + 1 > MAX_INLINE_DATA) + blocks++; + } else { + ASSERT(0); + } + + node_blk->i.i_mode = cpu_to_le16(mode); + node_blk->i.i_advise = 0; + node_blk->i.i_uid = cpu_to_le32(de->uid); + node_blk->i.i_gid = cpu_to_le32(de->gid); + node_blk->i.i_links = cpu_to_le32(links); + node_blk->i.i_size = cpu_to_le32(size); + node_blk->i.i_blocks = cpu_to_le32(blocks); + node_blk->i.i_atime = cpu_to_le64(de->mtime); + node_blk->i.i_ctime = cpu_to_le64(de->mtime); + node_blk->i.i_mtime = cpu_to_le64(de->mtime); + node_blk->i.i_atime_nsec = 0; + node_blk->i.i_ctime_nsec = 0; + node_blk->i.i_mtime_nsec = 0; + node_blk->i.i_generation = 0; + node_blk->i.i_current_depth = cpu_to_le32(1); + node_blk->i.i_xattr_nid = 0; + node_blk->i.i_flags = 0; + node_blk->i.i_inline = F2FS_INLINE_XATTR; + node_blk->i.i_pino = cpu_to_le32(de->pino); + node_blk->i.i_namelen = cpu_to_le32(de->len); + memcpy(node_blk->i.i_name, de->name, de->len); + node_blk->i.i_name[de->len] = 0; + + node_blk->footer.ino = cpu_to_le32(de->ino); + node_blk->footer.nid = cpu_to_le32(de->ino); + node_blk->footer.flag = 0; + node_blk->footer.cp_ver = ckpt->checkpoint_ver; + + if (S_ISDIR(mode)) + make_empty_dir(sbi, node_blk); + else if (S_ISLNK(mode)) + page_symlink(sbi, node_blk, de->link, size); +} + +int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de) +{ + struct f2fs_node *parent, *child; + struct node_info ni; + struct f2fs_summary sum; + block_t blkaddr; + int ret; + + /* Find if there is a */ + get_node_info(sbi, de->pino, &ni); + if (ni.blk_addr == NULL_ADDR) { + MSG(0, "No parent directory pino=%x\n", de->pino); + return -1; + } + + parent = calloc(BLOCK_SZ, 1); + ASSERT(parent); + + ret = dev_read_block(parent, ni.blk_addr); + ASSERT(ret >= 0); + + ret = f2fs_find_entry(sbi, parent, de); + if (ret) { + MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n", + de->name, de->pino, ret); + if (de->file_type == F2FS_FT_REG_FILE) + de->ino = 0; + goto free_parent_dir; + } + + child = calloc(BLOCK_SZ, 1); + ASSERT(child); + + f2fs_alloc_nid(sbi, &de->ino, 1); + + init_inode_block(sbi, child, de); + + ret = f2fs_add_link(sbi, parent, child, ni.blk_addr); + if (ret) { + MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n", + de->name, de->pino, ret); + goto free_child_dir; + } + + /* write child */ + set_summary(&sum, de->ino, 0, ni.version); + reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE); + + /* update nat info */ + update_nat_blkaddr(sbi, de->ino, de->ino, blkaddr); + + ret = dev_write_block(child, blkaddr); + ASSERT(ret >= 0); + + update_free_segments(sbi); + MSG(1, "Info: Create \"%s\" type=%x, ino=%x / %x into \"%s\"\n", + de->full_path, de->file_type, + de->ino, de->pino, de->path); +free_child_dir: + free(child); +free_parent_dir: + free(parent); + return 0; +} + +int f2fs_mkdir(struct f2fs_sb_info *sbi, struct dentry *de) +{ + return f2fs_create(sbi, de); +} + +int f2fs_symlink(struct f2fs_sb_info *sbi, struct dentry *de) +{ + return f2fs_create(sbi, de); +} + +int f2fs_find_path(struct f2fs_sb_info *sbi, char *path, nid_t *ino) +{ + struct f2fs_node *parent; + struct node_info ni; + struct dentry de; + int err = 0; + int ret; + char *p; + + if (path[0] != '/') + return -ENOENT; + + *ino = F2FS_ROOT_INO(sbi); + parent = calloc(BLOCK_SZ, 1); + ASSERT(parent); + + p = strtok(path, "/"); + while (p) { + de.name = (const u8 *)p; + de.len = strlen(p); + + get_node_info(sbi, *ino, &ni); + if (ni.blk_addr == NULL_ADDR) { + err = -ENOENT; + goto err; + } + ret = dev_read_block(parent, ni.blk_addr); + ASSERT(ret >= 0); + + ret = f2fs_find_entry(sbi, parent, &de); + if (!ret) { + err = -ENOENT; + goto err; + } + + *ino = de.ino; + p = strtok(NULL, "/"); + } +err: + free(parent); + return err; +} |