mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 10:10:00 +00:00
362f3171dc
As Jaegeuk reminded: Once user updates f2fs-tools which support new fields in inode layout, but do keep the kernel which can not support those fields, it will cause old f2fs fail to mount new image due to root_inode's i_extra_isize value sanity check. So if f2fs-tools doesn't enable feature which will use new fields of inode, we don't need to expand i_extra_isize to include them, let's just let i_extra_isize point to the end of last valid extra field's position. Signed-off-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
707 lines
18 KiB
C
707 lines
18 KiB
C
/**
|
|
* 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 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;
|
|
|
|
}
|
|
|
|
void make_dentry_ptr(struct f2fs_dentry_ptr *d, struct f2fs_node *node_blk,
|
|
void *src, int type)
|
|
{
|
|
if (type == 1) {
|
|
struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
|
|
d->max = NR_DENTRY_IN_BLOCK;
|
|
d->nr_bitmap = SIZE_OF_DENTRY_BITMAP;
|
|
d->bitmap = t->dentry_bitmap;
|
|
d->dentry = t->dentry;
|
|
d->filename = t->filename;
|
|
} else {
|
|
int entry_cnt = NR_INLINE_DENTRY(node_blk);
|
|
int bitmap_size = INLINE_DENTRY_BITMAP_SIZE(node_blk);
|
|
int reserved_size = INLINE_RESERVED_SIZE(node_blk);
|
|
|
|
d->max = entry_cnt;
|
|
d->nr_bitmap = bitmap_size;
|
|
d->bitmap = (u8 *)src;
|
|
d->dentry = (struct f2fs_dir_entry *)
|
|
((char *)src + bitmap_size + reserved_size);
|
|
d->filename = (__u8 (*)[F2FS_SLOT_LEN])((char *)src +
|
|
bitmap_size + reserved_size +
|
|
SIZE_OF_DIR_ENTRY * entry_cnt);
|
|
}
|
|
}
|
|
|
|
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 < (unsigned long)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, NULL, 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;
|
|
void *dentry_blk;
|
|
int max_slots = 214;
|
|
nid_t ino = le32_to_cpu(dir->footer.ino);
|
|
f2fs_hash_t namehash;
|
|
unsigned int dir_level = dir->i.i_dir_level;
|
|
int ret = 0;
|
|
|
|
namehash = f2fs_dentry_hash(de->name, de->len);
|
|
|
|
nbucket = dir_buckets(level, dir_level);
|
|
nblock = bucket_blocks(level);
|
|
|
|
bidx = dir_block_index(level, dir_level, le32_to_cpu(namehash) % nbucket);
|
|
end_block = bidx + nblock;
|
|
|
|
dentry_blk = calloc(BLOCK_SZ, 1);
|
|
ASSERT(dentry_blk);
|
|
|
|
memset(&dn, 0, sizeof(dn));
|
|
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 = le32_to_cpu(dir->i.i_current_depth);
|
|
for (level = 0; level < max_depth; level ++) {
|
|
if (find_in_level(sbi, dir, level, de))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* return ino if file exists, otherwise return 0 */
|
|
nid_t f2fs_lookup(struct f2fs_sb_info *sbi, struct f2fs_node *dir,
|
|
u8 *name, int len)
|
|
{
|
|
int err;
|
|
struct dentry de = {
|
|
.name = name,
|
|
.len = len,
|
|
};
|
|
|
|
err = f2fs_find_entry(sbi, dir, &de);
|
|
if (err == 1)
|
|
return de.ino;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void f2fs_update_dentry(nid_t ino, int file_type,
|
|
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 = cpu_to_le16(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);
|
|
de->file_type = file_type;
|
|
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.
|
|
*/
|
|
int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
|
|
const unsigned char *name, int name_len, nid_t ino,
|
|
int file_type, block_t p_blkaddr, int inc_link)
|
|
{
|
|
int level = 0, current_depth, bit_pos;
|
|
int nbucket, nblock, bidx, block;
|
|
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;
|
|
nid_t pino = le32_to_cpu(parent->footer.ino);
|
|
unsigned int dir_level = parent->i.i_dir_level;
|
|
int ret;
|
|
|
|
if (parent == 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, dir_level);
|
|
nblock = bucket_blocks(level);
|
|
bidx = dir_block_index(level, dir_level, le32_to_cpu(dentry_hash) % nbucket);
|
|
|
|
memset(&dn, 0, sizeof(dn));
|
|
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, NULL, (void *)dentry_blk, 1);
|
|
f2fs_update_dentry(ino, file_type, &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 (inc_link && (file_type == F2FS_FT_DIR)){
|
|
u32 links = le32_to_cpu(parent->i.i_links);
|
|
parent->i.i_links = cpu_to_le32(links + 1);
|
|
dn.idirty = 1;
|
|
}
|
|
|
|
if ((__u64)((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 = NULL_ADDR;
|
|
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[get_extra_isize(inode)] = 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 = NULL_ADDR;
|
|
int ret;
|
|
|
|
get_node_info(sbi, ino, &ni);
|
|
|
|
/* store into inline_data */
|
|
if ((unsigned long)(symlen + 1) <= MAX_INLINE_DATA(inode)) {
|
|
inode->i.i_inline |= F2FS_INLINE_DATA;
|
|
inode->i.i_inline |= F2FS_DATA_EXIST;
|
|
memcpy(inline_data_addr(inode), 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[get_extra_isize(inode)] = 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(node_blk))
|
|
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;
|
|
if (de->file_type == F2FS_FT_DIR)
|
|
node_blk->i.i_current_depth = cpu_to_le32(1);
|
|
else
|
|
node_blk->i.i_current_depth = cpu_to_le32(0);
|
|
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;
|
|
|
|
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());
|
|
}
|
|
|
|
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);
|
|
|
|
if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
|
|
node_blk->i.i_inode_checksum =
|
|
cpu_to_le32(f2fs_inode_chksum(node_blk));
|
|
}
|
|
|
|
int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
|
|
block_t p_blkaddr)
|
|
{
|
|
struct f2fs_inode *inode = &(node->i);
|
|
unsigned int dir_level = node->i.i_dir_level;
|
|
nid_t ino = le32_to_cpu(node->footer.ino);
|
|
char inline_data[MAX_INLINE_DATA(node)];
|
|
struct dnode_of_data dn;
|
|
struct f2fs_dentry_ptr d;
|
|
unsigned long bit_pos = 0;
|
|
int ret = 0;
|
|
|
|
if (!(inode->i_inline & F2FS_INLINE_DENTRY))
|
|
return 0;
|
|
|
|
memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA(node));
|
|
memset(inline_data_addr(node), 0, MAX_INLINE_DATA(node));
|
|
inode->i_inline &= ~F2FS_INLINE_DENTRY;
|
|
|
|
ret = dev_write_block(node, p_blkaddr);
|
|
ASSERT(ret >= 0);
|
|
|
|
memset(&dn, 0, sizeof(dn));
|
|
if (!dir_level) {
|
|
struct f2fs_dentry_block *dentry_blk;
|
|
struct f2fs_dentry_ptr src, dst;
|
|
|
|
dentry_blk = calloc(BLOCK_SZ, 1);
|
|
ASSERT(dentry_blk);
|
|
|
|
set_new_dnode(&dn, node, NULL, ino);
|
|
get_dnode_of_data(sbi, &dn, 0, ALLOC_NODE);
|
|
if (dn.data_blkaddr == NULL_ADDR)
|
|
new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
|
|
|
|
make_dentry_ptr(&src, node, (void *)inline_data, 2);
|
|
make_dentry_ptr(&dst, NULL, (void *)dentry_blk, 1);
|
|
|
|
/* copy data from inline dentry block to new dentry block */
|
|
memcpy(dst.bitmap, src.bitmap, src.nr_bitmap);
|
|
memset(dst.bitmap + src.nr_bitmap, 0,
|
|
dst.nr_bitmap - src.nr_bitmap);
|
|
|
|
memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max);
|
|
memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN);
|
|
|
|
ret = dev_write_block(dentry_blk, dn.data_blkaddr);
|
|
ASSERT(ret >= 0);
|
|
|
|
MSG(1, "%s: copy inline entry to block\n", __func__);
|
|
|
|
free(dentry_blk);
|
|
return ret;
|
|
}
|
|
|
|
make_empty_dir(sbi, node);
|
|
make_dentry_ptr(&d, node, (void *)inline_data, 2);
|
|
|
|
while (bit_pos < (unsigned long)d.max) {
|
|
struct f2fs_dir_entry *de;
|
|
const unsigned char *filename;
|
|
int namelen;
|
|
|
|
if (!test_bit_le(bit_pos, d.bitmap)) {
|
|
bit_pos++;
|
|
continue;
|
|
}
|
|
|
|
de = &d.dentry[bit_pos];
|
|
if (!de->name_len) {
|
|
bit_pos++;
|
|
continue;
|
|
}
|
|
|
|
filename = d.filename[bit_pos];
|
|
namelen = le32_to_cpu(de->name_len);
|
|
|
|
if (is_dot_dotdot(filename, namelen)) {
|
|
bit_pos += GET_DENTRY_SLOTS(namelen);
|
|
continue;
|
|
}
|
|
|
|
ret = f2fs_add_link(sbi, node, filename, namelen,
|
|
le32_to_cpu(de->ino),
|
|
de->file_type, p_blkaddr, 0);
|
|
if (ret)
|
|
MSG(0, "Convert file \"%s\" ERR=%d\n", filename, ret);
|
|
else
|
|
MSG(1, "%s: add inline entry to block\n", __func__);
|
|
|
|
bit_pos += GET_DENTRY_SLOTS(namelen);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 = NULL_ADDR;
|
|
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);
|
|
|
|
/* Must convert inline dentry before the following opertions */
|
|
ret = convert_inline_dentry(sbi, parent, ni.blk_addr);
|
|
if (ret) {
|
|
MSG(0, "Convert inline dentry for pino=%x failed.\n", de->pino);
|
|
return -1;
|
|
}
|
|
|
|
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->i.i_name,
|
|
le32_to_cpu(child->i.i_namelen),
|
|
le32_to_cpu(child->footer.ino),
|
|
map_de_type(le16_to_cpu(child->i.i_mode)),
|
|
ni.blk_addr, 1);
|
|
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 -> %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);
|
|
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;
|
|
}
|