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>
This commit is contained in:
Jaegeuk Kim 2015-12-09 16:18:44 -08:00
parent d3be08825e
commit 603f8f9d36
23 changed files with 2042 additions and 17 deletions

1
README
View File

@ -12,6 +12,7 @@ Your should install the following packages.
- pkg-config
- autoconf
- libtool
- libselinux1-dev
Initial compilation
-------------------

View File

@ -54,6 +54,7 @@ AC_PATH_PROG([LDCONFIG], [ldconfig],
# Checks for libraries.
PKG_CHECK_MODULES([libuuid], [uuid])
PKG_CHECK_MODULES([libselinux], [libselinux])
# Checks for header files.
AC_CHECK_HEADERS([linux/fs.h fcntl.h mntent.h stdlib.h string.h \

View File

@ -4,10 +4,12 @@ AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
sbin_PROGRAMS = fsck.f2fs
fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h \
resize.c
fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
resize.c \
node.c segment.c dir.c sload.c xattr.c
fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
install-data-hook:
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs

View File

@ -51,7 +51,7 @@ static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
le16_to_cpu(sum.ofs_in_node), to);
else
update_nat_blkaddr(sbi, le32_to_cpu(sum.nid), to);
update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
IS_DATASEG(type) ? "data" : "node",

584
fsck/dir.c Normal file
View File

@ -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;
}

View File

@ -60,6 +60,7 @@ struct f2fs_nm_info {
char *nat_bitmap;
int bitmap_size;
char *nid_bitmap;
};
struct seg_entry {
@ -124,6 +125,44 @@ struct f2fs_sm_info {
unsigned int ovp_segments;
};
struct f2fs_dentry_ptr {
struct inode *inode;
u8 *bitmap;
struct f2fs_dir_entry *dentry;
__u8 (*filename)[F2FS_SLOT_LEN];
int max;
};
struct dentry {
char *path;
char *full_path;
const u8 *name;
int len;
char *link;
unsigned long size;
u8 file_type;
u16 mode;
u16 uid;
u16 gid;
u32 *inode;
u32 mtime;
char *secon;
uint64_t capabilities;
nid_t ino;
nid_t pino;
};
/* different from dnode_of_data in kernel */
struct dnode_of_data {
struct f2fs_node *inode_blk; /* inode page */
struct f2fs_node *node_blk; /* cached direct node page */
nid_t nid;
unsigned int ofs_in_node;
block_t data_blkaddr;
block_t node_blkaddr;
int idirty, ndirty;
};
struct f2fs_sb_info {
struct f2fs_fsck *fsck;
@ -160,6 +199,7 @@ struct f2fs_sb_info {
u32 s_next_generation; /* for NFS support */
unsigned int cur_victim_sec; /* current victim section num */
u32 free_segments;
};
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
@ -313,7 +353,6 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
- (base + 1) + type;
}
#define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats))
#define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits))
@ -397,6 +436,42 @@ static inline void node_info_from_raw_nat(struct node_info *ni,
ni->version = raw_nat->version;
}
static inline void set_summary(struct f2fs_summary *sum, nid_t nid,
unsigned int ofs_in_node, unsigned char version)
{
sum->nid = cpu_to_le32(nid);
sum->ofs_in_node = cpu_to_le16(ofs_in_node);
sum->version = version;
}
#define S_SHIFT 12
static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
[S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE,
[S_IFDIR >> S_SHIFT] = F2FS_FT_DIR,
[S_IFCHR >> S_SHIFT] = F2FS_FT_CHRDEV,
[S_IFBLK >> S_SHIFT] = F2FS_FT_BLKDEV,
[S_IFIFO >> S_SHIFT] = F2FS_FT_FIFO,
[S_IFSOCK >> S_SHIFT] = F2FS_FT_SOCK,
[S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK,
};
static inline void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
{
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
}
static inline void *inline_xattr_addr(struct f2fs_inode *inode)
{
return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE_INLINE_XATTR]);
}
static inline int inline_xattr_size(struct f2fs_inode *inode)
{
if (inode->i_inline & F2FS_INLINE_XATTR)
return F2FS_INLINE_XATTR_ADDRS << 2;
return 0;
}
extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne);
#define IS_SUM_NODE_SEG(footer) (footer.entry_type == SUM_TYPE_NODE)
#define IS_SUM_DATA_SEG(footer) (footer.entry_type == SUM_TYPE_DATA)

View File

@ -108,6 +108,8 @@ enum seg_type {
SEG_TYPE_MAX,
};
struct selabel_handle;
extern void fsck_chk_orphan_node(struct f2fs_sb_info *);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
@ -129,6 +131,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
struct child_info *);
int fsck_chk_meta(struct f2fs_sb_info *sbi);
extern void update_free_segments(struct f2fs_sb_info *);
void print_cp_state(u32);
extern void print_node_info(struct f2fs_node *);
extern void print_inode_info(struct f2fs_inode *, int);
@ -157,7 +160,7 @@ extern void write_curseg_info(struct f2fs_sb_info *);
extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int);
extern void write_checkpoint(struct f2fs_sb_info *);
extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t);
extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, block_t);
extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
extern void print_raw_sb_info(struct f2fs_super_block *);
@ -181,4 +184,25 @@ int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
/* resize.c */
int f2fs_resize(struct f2fs_sb_info *);
/* sload.c */
int f2fs_sload(struct f2fs_sb_info *, const char *, const char *,
const char *, struct selabel_handle *);
void reserve_new_block(struct f2fs_sb_info *, block_t *,
struct f2fs_summary *, int);
void new_data_block(struct f2fs_sb_info *, void *,
struct dnode_of_data *, int);
int f2fs_build_file(struct f2fs_sb_info *, struct dentry *);
void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *, int);
void set_data_blkaddr(struct dnode_of_data *);
block_t new_node_block(struct f2fs_sb_info *,
struct dnode_of_data *, unsigned int);
void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
pgoff_t, int);
int f2fs_create(struct f2fs_sb_info *, struct dentry *);
int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *);
int f2fs_symlink(struct f2fs_sb_info *, struct dentry *);
int inode_set_selinux(struct f2fs_sb_info *, u32, const char *);
int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
#endif /* _FSCK_H_ */

View File

@ -5,6 +5,11 @@
* http://www.samsung.com/
* Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
* : implement defrag.f2fs
* Copyright (C) 2015 Huawei Ltd.
* Hou Pengyang <houpengyang@huawei.com>
* Liu Shuoran <liushuoran@huawei.com>
* Jaegeuk Kim <jaegeuk@kernel.org>
* : add sload.f2fs
*
* 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
@ -61,6 +66,16 @@ void resize_usage()
exit(1);
}
void sload_usage()
{
MSG(0, "\nUsage: sload.f2fs [options] device\n");
MSG(0, "[options]:\n");
MSG(0, " -f source directory [path of the source directory]\n");
MSG(0, " -t mount point [prefix of target fs path, default:/]\n");
MSG(0, " -d debug level [default:0]\n");
exit(1);
}
void f2fs_parse_options(int argc, char *argv[])
{
int option = 0;
@ -240,6 +255,29 @@ void f2fs_parse_options(int argc, char *argv[])
}
ASSERT(ret >= 0);
}
} else if (!strcmp("sload.f2fs", prog)) {
const char *option_string = "d:f:t:";
config.func = SLOAD;
while ((option = getopt(argc, argv, option_string)) != EOF) {
switch (option) {
case 'd':
config.dbg_lv = atoi(optarg);
MSG(0, "Info: Debug level = %d\n",
config.dbg_lv);
break;
case 'f':
config.from_dir = (char *)optarg;
break;
case 't':
config.mount_point = (char *)optarg;
break;
default:
MSG(0, "\tError: Unknown option %c\n", option);
sload_usage();
break;
}
}
}
if ((optind + 1) != argc) {
@ -252,6 +290,8 @@ void f2fs_parse_options(int argc, char *argv[])
defrag_usage();
else if (config.func == RESIZE)
resize_usage();
else if (config.func == SLOAD)
sload_usage();
}
config.device_name = argv[optind];
}
@ -405,6 +445,19 @@ static int do_resize(struct f2fs_sb_info *sbi)
return f2fs_resize(sbi);
}
static int do_sload(struct f2fs_sb_info *sbi)
{
if (!config.from_dir) {
MSG(0, "\tError: Need source directory\n");
sload_usage();
return -1;
}
if (!config.mount_point)
config.mount_point = "/";
return f2fs_sload(sbi, config.from_dir, config.mount_point, NULL, NULL);
}
int main(int argc, char **argv)
{
struct f2fs_sb_info *sbi;
@ -459,6 +512,9 @@ fsck_again:
if (do_resize(sbi))
goto out_err;
break;
case SLOAD:
do_sload(sbi);
break;
}
f2fs_do_umount(sbi);

View File

@ -25,6 +25,16 @@ static u32 get_free_segments(struct f2fs_sb_info *sbi)
return free_segs;
}
void update_free_segments(struct f2fs_sb_info *sbi)
{
char *progress = "-*|*-";
static int i = 0;
MSG(0, "\r [ %c ] Free segments: 0x%x", progress[i % 5], get_free_segments(sbi));
fflush(stdout);
i++;
}
void print_inode_info(struct f2fs_inode *inode, int name)
{
unsigned int i = 0;
@ -624,6 +634,74 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
return 0;
}
static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
pgoff_t block_off;
pgoff_t block_addr;
int seg_off;
block_off = NAT_BLOCK_OFFSET(start);
seg_off = block_off >> sbi->log_blocks_per_seg;
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
(seg_off << sbi->log_blocks_per_seg << 1) +
(block_off & ((1 << sbi->log_blocks_per_seg) -1)));
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
block_addr += sbi->blocks_per_seg;
return block_addr;
}
static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
int nid_bitmap_size = (nm_i->max_nid + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk;
struct f2fs_journal *journal = &sum->journal;
struct f2fs_nat_block nat_block;
block_t start_blk;
nid_t nid;
int i;
if (!(config.func == SLOAD))
return 0;
nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1);
if (!nm_i->nid_bitmap)
return -ENOMEM;
/* arbitrarily set 0 bit */
f2fs_set_bit(0, nm_i->nid_bitmap);
memset((void *)&nat_block, 0, sizeof(struct f2fs_nat_block));
for (nid = 0; nid < nm_i->max_nid; nid++) {
if (!(nid % NAT_ENTRY_PER_BLOCK)) {
int ret;
start_blk = current_nat_addr(sbi, nid);
ret = dev_read_block((void *)&nat_block, start_blk);
ASSERT(ret >= 0);
}
if (nat_block.entries[nid % NAT_ENTRY_PER_BLOCK].block_addr)
f2fs_set_bit(nid, nm_i->nid_bitmap);
}
for (i = 0; i < nats_in_cursum(journal); i++) {
block_t addr;
addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
nid = le32_to_cpu(nid_in_journal(journal, i));
if (addr != NULL_ADDR)
f2fs_set_bit(nid, nm_i->nid_bitmap);
}
return 0;
}
int init_node_manager(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
@ -654,7 +732,7 @@ int init_node_manager(struct f2fs_sb_info *sbi)
/* copy version bitmap */
memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size);
return 0;
return f2fs_init_nid_bitmap(sbi);
}
int build_node_manager(struct f2fs_sb_info *sbi)
@ -1171,7 +1249,8 @@ void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid,
free(node_blk);
}
void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, block_t newaddr)
void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino,
nid_t nid, block_t newaddr)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_nat_block *nat_block;
@ -1196,6 +1275,8 @@ void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, block_t newaddr)
ret = dev_read_block(nat_block, block_addr);
ASSERT(ret >= 0);
if (ino)
nat_block->entries[entry_off].ino = cpu_to_le32(ino);
nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr);
ret = dev_write_block(nat_block, block_addr);
@ -1567,7 +1648,7 @@ void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
/* update se->types */
reset_curseg(sbi, i);
DBG(0, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
DBG(1, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
i, old_segno, curseg->segno, from);
}
}
@ -1682,6 +1763,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi)
set_cp(ckpt_flags, flags);
set_cp(free_segment_count, get_free_segments(sbi));
set_cp(valid_block_count, sbi->total_valid_block_count);
set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
@ -1915,6 +1997,8 @@ void f2fs_do_umount(struct f2fs_sb_info *sbi)
unsigned int i;
/* free nm_info */
if (config.func == SLOAD)
free(nm_i->nid_bitmap);
free(nm_i->nat_bitmap);
free(sbi->nm_info);

250
fsck/node.c Normal file
View File

@ -0,0 +1,250 @@
/**
* node.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 f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid, int inode)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
nid_t i, inode_cnt, node_cnt;
for (i = 0; i < nm_i->max_nid; i++)
if(f2fs_test_bit(i, nm_i->nid_bitmap) == 0)
break;
ASSERT(i < nm_i->max_nid);
f2fs_set_bit(i, nm_i->nid_bitmap);
*nid = i;
inode_cnt = get_cp(valid_inode_count);
node_cnt = get_cp(valid_node_count);
if (inode)
set_cp(valid_inode_count, inode_cnt + 1);
set_cp(valid_node_count, node_cnt + 1);
}
void set_data_blkaddr(struct dnode_of_data *dn)
{
__le32 *addr_array;
struct f2fs_node *node_blk = dn->node_blk;
unsigned int ofs_in_node = dn->ofs_in_node;
addr_array = blkaddr_in_node(node_blk);
addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
if (dn->node_blk != dn->inode_blk)
dn->ndirty = 1;
else
dn->idirty = 1;
}
/*
* In this function, we get a new node blk, and write back
* node_blk would be sloadd in RAM, linked by dn->node_blk
*/
block_t new_node_block(struct f2fs_sb_info *sbi,
struct dnode_of_data *dn, unsigned int ofs)
{
struct f2fs_node *f2fs_inode;
struct f2fs_node *node_blk;
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
struct f2fs_summary sum;
struct node_info ni;
block_t blkaddr;
int type;
f2fs_inode = dn->inode_blk;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
node_blk->footer.nid = cpu_to_le32(dn->nid);
node_blk->footer.ino = f2fs_inode->footer.ino;
node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
node_blk->footer.cp_ver = ckpt->checkpoint_ver;
type = CURSEG_COLD_NODE;
if (IS_DNODE(node_blk)) {
if (S_ISDIR(f2fs_inode->i.i_mode))
type = CURSEG_HOT_NODE;
else
type = CURSEG_WARM_NODE;
}
get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, 0, ni.version);
reserve_new_block(sbi, &blkaddr, &sum, type);
/* update nat info */
update_nat_blkaddr(sbi, le32_to_cpu(f2fs_inode->footer.ino),
dn->nid, blkaddr);
dn->node_blk = node_blk;
inc_inode_blocks(dn);
return blkaddr;
}
/*
* get_node_path - Get the index path of pgoff_t block
* @offset: offset in the current index node block.
* @noffset: NO. of the index block within a file.
* return: depth of the index path.
*
* By default, it sets inline_xattr and inline_data
*/
static int get_node_path(unsigned long block,
int offset[4], unsigned int noffset[4])
{
const long direct_index = DEF_ADDRS_PER_INODE_INLINE_XATTR;
const long direct_blks = ADDRS_PER_BLOCK;
const long dptrs_per_blk = NIDS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;
int n = 0;
int level = 0;
noffset[0] = 0;
if (block < direct_index) {
offset[n] = block;
goto got;
}
block -= direct_index;
if (block < direct_blks) {
offset[n++] = NODE_DIR1_BLOCK;
noffset[n]= 1;
offset[n] = block;
level = 1;
goto got;
}
block -= direct_blks;
if (block < direct_blks) {
offset[n++] = NODE_DIR2_BLOCK;
noffset[n] = 2;
offset[n] = block;
level = 1;
goto got;
}
block -= direct_blks;
if (block < indirect_blks) {
offset[n++] = NODE_IND1_BLOCK;
noffset[n] = 3;
offset[n++] = block / direct_blks;
noffset[n] = 4 + offset[n - 1];
offset[n] = block % direct_blks;
level = 2;
goto got;
}
block -= indirect_blks;
if (block < indirect_blks) {
offset[n++] = NODE_IND2_BLOCK;
noffset[n] = 4 + dptrs_per_blk;
offset[n++] = block / direct_blks;
noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
offset[n] = block % direct_blks;
level = 2;
goto got;
}
block -= indirect_blks;
if (block < dindirect_blks) {
offset[n++] = NODE_DIND_BLOCK;
noffset[n] = 5 + (dptrs_per_blk * 2);
offset[n++] = block / indirect_blks;
noffset[n] = 6 + (dptrs_per_blk * 2) +
offset[n - 1] * (dptrs_per_blk + 1);
offset[n++] = (block / direct_blks) % dptrs_per_blk;
noffset[n] = 7 + (dptrs_per_blk * 2) +
offset[n - 2] * (dptrs_per_blk + 1) +
offset[n - 1];
offset[n] = block % direct_blks;
level = 3;
goto got;
} else {
ASSERT(0);
}
got:
return level;
}
void get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
pgoff_t index, int mode)
{
int offset[4];
unsigned int noffset[4];
struct f2fs_node *parent = NULL;
nid_t nids[4];
block_t nblk[4];
struct node_info ni;
int level, i;
int ret;
level = get_node_path(index, offset, noffset);
nids[0] = dn->nid;
parent = dn->inode_blk;
if (level != 0)
nids[1] = get_nid(parent, offset[0], 1);
else
dn->node_blk = dn->inode_blk;
get_node_info(sbi, nids[0], &ni);
nblk[0] = ni.blk_addr;
for (i = 1; i <= level; i++) {
if (!nids[i] && mode == ALLOC_NODE) {
f2fs_alloc_nid(sbi, &nids[i], 0);
dn->nid = nids[i];
/* Function new_node_blk get a new f2fs_node blk and update*/
/* We should make sure that dn->node_blk == NULL*/
nblk[i] = new_node_block(sbi, dn, noffset[i]);
ASSERT(nblk[i]);
set_nid(parent, offset[i - 1], nids[i], i == 1);
} else {
/* If Sparse file no read API, */
struct node_info ni;
get_node_info(sbi, nids[i], &ni);
dn->node_blk = calloc(BLOCK_SZ, 1);
ASSERT(dn->node_blk);
ret = dev_read_block(dn->node_blk, ni.blk_addr);
ASSERT(ret >= 0);
nblk[i] = ni.blk_addr;
}
if (mode == ALLOC_NODE){
/* Parent node may have changed */
ret = dev_write_block(parent, nblk[i - 1]);
ASSERT(ret >= 0);
}
if (i != 1)
free(parent);
if (i < level) {
parent = dn->node_blk;
nids[i + 1] = get_nid(parent, offset[i], 0);
}
}
dn->nid = nids[level];
dn->ofs_in_node = offset[level];
dn->data_blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
dn->node_blkaddr = nblk[level];
}

101
fsck/node.h Normal file
View File

@ -0,0 +1,101 @@
/**
* node.h
*
* 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.
*/
#ifndef _NODE_H_
#define _NODE_H_
#include "fsck.h"
#define ADDRS_PER_PAGE(page) \
(IS_INODE(page) ? ADDRS_PER_INODE(&page->i) : ADDRS_PER_BLOCK)
static inline int IS_INODE(struct f2fs_node *node)
{
return ((node)->footer.nid == (node)->footer.ino);
}
static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
{
return IS_INODE(node) ? node->i.i_addr : node->dn.addr;
}
static inline block_t datablock_addr(struct f2fs_node *node_page,
unsigned int offset)
{
__le32 *addr_array;
ASSERT(node_page);
addr_array = blkaddr_in_node(node_page);
return le32_to_cpu(addr_array[offset]);
}
static inline void set_nid(struct f2fs_node * rn, int off, nid_t nid, int i)
{
if (i)
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
else
rn->in.nid[off] = cpu_to_le32(nid);
}
static inline nid_t get_nid(struct f2fs_node * rn, int off, int i)
{
if (i)
return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
else
return le32_to_cpu(rn->in.nid[off]);
}
enum {
ALLOC_NODE, /* allocate a new node page if needed */
LOOKUP_NODE, /* lookup up a node without readahead */
LOOKUP_NODE_RA,
};
static inline void set_new_dnode(struct dnode_of_data *dn,
struct f2fs_node *iblk, struct f2fs_node *nblk, nid_t nid)
{
memset(dn, 0, sizeof(*dn));
dn->inode_blk = iblk;
dn->node_blk = nblk;
dn->nid = nid;
dn->idirty = 0;
dn->ndirty = 0;
}
static inline void inc_inode_blocks(struct dnode_of_data *dn)
{
u64 blocks = le64_to_cpu(dn->inode_blk->i.i_blocks);
dn->inode_blk->i.i_blocks = cpu_to_le64(blocks + 1);
dn->idirty = 1;
}
static inline int IS_DNODE(struct f2fs_node *node_page)
{
unsigned int ofs = ofs_of_node(node_page);
if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
ofs == 5 + 2 * NIDS_PER_BLOCK)
return 0;
if (ofs >= 6 + 2 * NIDS_PER_BLOCK) {
ofs -= 6 + 2 * NIDS_PER_BLOCK;
if (!((long int)ofs % (NIDS_PER_BLOCK + 1)))
return 0;
}
return 1;
}
#endif

220
fsck/segment.c Normal file
View File

@ -0,0 +1,220 @@
/**
* 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(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;
}

247
fsck/sload.c Normal file
View File

@ -0,0 +1,247 @@
/**
* sload.c
*
* 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.
*/
#define _GNU_SOURCE
#include "fsck.h"
#include <libgen.h>
#include <dirent.h>
#include <mntent.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#ifdef WITH_ANDROID
#include <selinux/label.h>
#include <private/android_filesystem_config.h>
static void handle_selabel(struct dentry *de, int dir, char *target_out)
{
uint64_t capabilities;
unsigned int mode = 0;
unsigned int uid = 0;
unsigned int gid = 0;
fs_config(de[i].path, dir, target_out, &uid,
&gid, &mode, &capabilities);
de.mode = mode;
de.uid = uid;
de.gid = gid;
de.capabilities = capabilities;
}
#else
#define handle_selabel(...)
#endif
static int filter_dot(const struct dirent *d)
{
return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
}
static void f2fs_make_directory(struct f2fs_sb_info *sbi,
int entries, struct dentry *de)
{
int i = 0;
for (i = 0; i < entries; i++) {
if (de[i].file_type == F2FS_FT_DIR)
f2fs_mkdir(sbi, de + i);
else if (de[i].file_type == F2FS_FT_REG_FILE)
f2fs_create(sbi, de + i);
else if (de[i].file_type == F2FS_FT_SYMLINK)
f2fs_symlink(sbi, de + i);
}
}
static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
const char *dir_path, const char *target_out_dir,
nid_t dir_ino, struct selabel_handle *sehnd)
{
int entries = 0;
struct dentry *dentries;
struct dirent **namelist = NULL;
struct stat stat;
int i, ret = 0;
entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
if (entries < 0) {
ERR_MSG("No entries in %s\n", full_path);
return -ENOENT;
}
dentries = calloc(entries, sizeof(struct dentry));
if (dentries == NULL)
return -ENOMEM;
for (i = 0; i < entries; i++) {
dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name);
if (dentries[i].name == NULL) {
ERR_MSG("Skip: ENOMEM\n");
continue;
}
dentries[i].len = strlen((char *)dentries[i].name);
ret = asprintf(&dentries[i].path, "%s/%s",
dir_path, namelist[i]->d_name);
ASSERT(ret > 0);
ret = asprintf(&dentries[i].full_path, "%s/%s",
full_path, namelist[i]->d_name);
ASSERT(ret > 0);
free(namelist[i]);
ret = lstat(dentries[i].full_path, &stat);
if (ret < 0) {
ERR_MSG("Skip: lstat failure\n");
continue;
}
dentries[i].size = stat.st_size;
dentries[i].mode = stat.st_mode &
(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
dentries[i].mtime = stat.st_mtime;
handle_selabel(dentries + i, S_ISDIR(stat.st_mode),
target_out_dir);
if (sehnd && selabel_lookup(sehnd, &dentries[i].secon,
dentries[i].path, stat.st_mode) < 0)
ERR_MSG("Cannot lookup security context for %s\n",
dentries[i].path);
dentries[i].pino = dir_ino;
if (S_ISREG(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_REG_FILE;
} else if (S_ISDIR(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_DIR;
} else if (S_ISCHR(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_CHRDEV;
} else if (S_ISBLK(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_BLKDEV;
} else if (S_ISFIFO(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_FIFO;
} else if (S_ISSOCK(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_SOCK;
} else if (S_ISLNK(stat.st_mode)) {
dentries[i].file_type = F2FS_FT_SYMLINK;
dentries[i].link = calloc(F2FS_BLKSIZE, 1);
ASSERT(dentries[i].link);
ret = readlink(dentries[i].full_path,
dentries[i].link, F2FS_BLKSIZE - 1);
ASSERT(ret >= 0);
} else {
MSG(1, "unknown file type on %s", dentries[i].path);
i--;
entries--;
}
}
free(namelist);
f2fs_make_directory(sbi, entries, dentries);
for (i = 0; i < entries; i++) {
if (dentries[i].file_type == F2FS_FT_REG_FILE) {
f2fs_build_file(sbi, dentries + i);
} else if (dentries[i].file_type == F2FS_FT_DIR) {
char *subdir_full_path = NULL;
char *subdir_dir_path;
ret = asprintf(&subdir_full_path, "%s/",
dentries[i].full_path);
ASSERT(ret > 0);
ret = asprintf(&subdir_dir_path, "%s/",
dentries[i].path);
ASSERT(ret > 0);
build_directory(sbi, subdir_full_path, subdir_dir_path,
target_out_dir, dentries[i].ino, sehnd);
free(subdir_full_path);
free(subdir_dir_path);
} else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
/*
* It is already done in f2fs_make_directory
* f2fs_make_symlink(sbi, dir_ino, &dentries[i]);
*/
} else {
MSG(1, "Error unknown file type\n");
}
if (dentries[i].secon) {
inode_set_selinux(sbi, dentries[i].ino, dentries[i].secon);
MSG(1, "File = %s \n----->SELinux context = %s\n",
dentries[i].path, dentries[i].secon);
MSG(1, "----->mode = 0x%x, uid = 0x%x, gid = 0x%x, "
"capabilities = 0x%lx \n",
dentries[i].mode, dentries[i].uid,
dentries[i].gid, dentries[i].capabilities);
}
free(dentries[i].path);
free(dentries[i].full_path);
free((void *)dentries[i].name);
free(dentries[i].secon);
}
free(dentries);
return 0;
}
int f2fs_sload(struct f2fs_sb_info *sbi, const char *from_dir,
const char *mount_point,
const char *target_out_dir,
struct selabel_handle *sehnd)
{
int ret = 0;
nid_t mnt_ino = F2FS_ROOT_INO(sbi);
/* flush NAT/SIT journal entries */
flush_journal_entries(sbi);
ret = f2fs_find_path(sbi, (char *)mount_point, &mnt_ino);
if (ret) {
ERR_MSG("Failed to get mount point %s\n", mount_point);
return ret;
}
ret = build_directory(sbi, from_dir, mount_point, target_out_dir,
mnt_ino, sehnd);
if (ret) {
ERR_MSG("Failed to build due to %d\n", ret);
return ret;
}
if (sehnd) {
char *secontext = NULL;
/* set root inode selinux context */
if (selabel_lookup(sehnd, &secontext, mount_point, S_IFDIR) < 0)
ERR_MSG("cannot lookup security context for %s\n",
mount_point);
if (secontext) {
MSG(1, "Labeling %s as %s, root_ino = %d\n",
mount_point, secontext, F2FS_ROOT_INO(sbi));
/* xattr_add for root inode */
inode_set_selinux(sbi, F2FS_ROOT_INO(sbi), secontext);
}
free(secontext);
}
/* update curseg info; can update sit->types */
move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
zero_journal_entries(sbi);
write_curseg_info(sbi);
/* flush dirty sit entries */
flush_sit_entries(sbi);
write_checkpoint(sbi);
return 0;
}

241
fsck/xattr.c Normal file
View File

@ -0,0 +1,241 @@
/**
* xattr.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"
#include "xattr.h"
#define XATTR_CREATE 0x1
#define XATTR_REPLACE 0x2
static void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
{
struct f2fs_xattr_header *header;
void *txattr_addr;
u64 inline_size = inline_xattr_size(&inode->i);
txattr_addr = calloc(inline_size + BLOCK_SZ, 1);
ASSERT(txattr_addr);
if (inline_size)
memcpy(txattr_addr, inline_xattr_addr(&inode->i), inline_size);
/* Read from xattr node block. */
if (inode->i.i_xattr_nid) {
struct node_info ni;
int ret;
get_node_info(sbi, le32_to_cpu(inode->i.i_xattr_nid), &ni);
ret = dev_read_block(txattr_addr + inline_size, ni.blk_addr);
ASSERT(ret >= 0);
}
header = XATTR_HDR(txattr_addr);
/* Never been allocated xattrs */
if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
header->h_refcount = cpu_to_le32(1);
}
return txattr_addr;
}
static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
size_t len, const char *name)
{
struct f2fs_xattr_entry *entry;
list_for_each_xattr(entry, base_addr) {
if (entry->e_name_index != index)
continue;
if (entry->e_name_len != len)
continue;
if (!memcmp(entry->e_name, name, len))
break;
}
return entry;
}
static void write_all_xattrs(struct f2fs_sb_info *sbi,
struct f2fs_node *inode, __u32 hsize, void *txattr_addr)
{
void *xattr_addr;
struct dnode_of_data dn;
struct node_info ni;
struct f2fs_node *xattr_node;
nid_t new_nid = 0;
block_t blkaddr;
nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
u64 inline_size = inline_xattr_size(&inode->i);
int ret;
ASSERT(inode->i.i_inline & F2FS_INLINE_XATTR);
memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size);
if (hsize <= inline_size)
return;
if (!xnid) {
f2fs_alloc_nid(sbi, &new_nid, 0);
set_new_dnode(&dn, inode, NULL, new_nid);
/* NAT entry would be updated by new_node_page. */
blkaddr = new_node_block(sbi, &dn, XATTR_NODE_OFFSET);
ASSERT(dn.node_blk);
xattr_node = dn.node_blk;
inode->i.i_xattr_nid = cpu_to_le32(new_nid);
} else {
set_new_dnode(&dn, inode, NULL, xnid);
get_node_info(sbi, xnid, &ni);
blkaddr = ni.blk_addr;
xattr_node = calloc(BLOCK_SZ, 1);
ASSERT(xattr_node);
ret = dev_read_block(xattr_node, ni.blk_addr);
ASSERT(ret >= 0);
}
/* write to xattr node block */
xattr_addr = (void *)xattr_node;
memcpy(xattr_addr, txattr_addr + inline_size,
PAGE_SIZE - sizeof(struct node_footer));
ret = dev_write_block(xattr_node, blkaddr);
ASSERT(ret >= 0);
}
int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *name,
const void *value, size_t size, int flags)
{
struct f2fs_node *inode;
void *base_addr;
struct f2fs_xattr_entry *here, *last;
struct node_info ni;
int error = 0;
int len;
int found, newsize;
__u32 new_hsize;
int ret;
if (name == NULL)
return -EINVAL;
if (value == NULL)
return -EINVAL;
len = strlen(name);
if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN)
return -ERANGE;
if (ino < 3)
return -EINVAL;
/* Now We just support selinux */
ASSERT(index == F2FS_XATTR_INDEX_SECURITY);
get_node_info(sbi, ino, &ni);
inode = calloc(BLOCK_SZ, 1);
ASSERT(inode);
ret = dev_read_block(inode, ni.blk_addr);
ASSERT(ret >= 0);
base_addr = read_all_xattrs(sbi, inode);
ASSERT(base_addr);
here = __find_xattr(base_addr, index, len, name);
found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
if ((flags & XATTR_REPLACE) && !found) {
error = -ENODATA;
goto exit;
} else if ((flags & XATTR_CREATE) && found) {
error = -EEXIST;
goto exit;
}
last = here;
while (!IS_XATTR_LAST_ENTRY(last))
last = XATTR_NEXT_ENTRY(last);
newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size);
/* 1. Check space */
if (value) {
int free;
/*
* If value is NULL, it is remove operation.
* In case of update operation, we calculate free.
*/
free = MIN_OFFSET - ((char *)last - (char *)base_addr);
if (found)
free = free + ENTRY_SIZE(here);
if (free < newsize) {
error = -ENOSPC;
goto exit;
}
}
/* 2. Remove old entry */
if (found) {
/*
* If entry if sound, remove old entry.
* If not found, remove operation is not needed
*/
struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
int oldsize = ENTRY_SIZE(here);
memmove(here, next, (char *)last - (char *)next);
last = (struct f2fs_xattr_entry *)((char *)last - oldsize);
memset(last, 0, oldsize);
}
new_hsize = (char *)last - (char *)base_addr;
/* 3. Write new entry */
if (value) {
char *pval;
/*
* Before we come here, old entry is removed.
* We just write new entry.
*/
memset(last, 0, newsize);
last->e_name_index = index;
last->e_name_len = len;
memcpy(last->e_name, name, len);
pval = last->e_name + len;
memcpy(pval, value, size);
last->e_value_size = cpu_to_le16(size);
new_hsize += newsize;
}
write_all_xattrs(sbi, inode, new_hsize, base_addr);
/* inode need update */
ret = dev_write_block(inode, ni.blk_addr);
ASSERT(ret >= 0);
exit:
free(base_addr);
return error;
}
int inode_set_selinux(struct f2fs_sb_info *sbi, u32 ino, const char *secon)
{
if (!secon)
return 0;
return f2fs_setxattr(sbi, ino, F2FS_XATTR_INDEX_SECURITY,
XATTR_SELINUX_SUFFIX, secon, strlen(secon), 1);
}

66
fsck/xattr.h Normal file
View File

@ -0,0 +1,66 @@
/**
* xattr.h
*
* 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.
*/
#ifndef _XATTR_H_
#define _XATTR_H_
#include <f2fs_fs.h>
#include "f2fs.h"
struct f2fs_xattr_header {
__le32 h_magic; /* magic number for identification */
__le32 h_refcount; /* reference count */
__u32 h_sloadd[4]; /* zero right now */
};
struct f2fs_xattr_entry {
__u8 e_name_index;
__u8 e_name_len;
__le16 e_value_size; /* size of attribute value */
char e_name[0]; /* attribute name */
};
#define XATTR_ROUND (3)
#define XATTR_SELINUX_SUFFIX "selinux"
#define F2FS_XATTR_INDEX_SECURITY 6
#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr))
#define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr))
#define F2FS_XATTR_MAGIC 0xF2F52011
#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *) ((char *)(entry) +\
ENTRY_SIZE(entry)))
#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1))
#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND)
#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \
entry->e_name_len + le16_to_cpu(entry->e_value_size)))
#define list_for_each_xattr(entry, addr) \
for (entry = XATTR_FIRST_ENTRY(addr); \
!IS_XATTR_LAST_ENTRY(entry); \
entry = XATTR_NEXT_ENTRY(entry))
#define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \
sizeof(struct node_footer) - sizeof(__u32))
#define MAX_VALUE_LEN (MIN_OFFSET - \
sizeof(struct f2fs_xattr_header) - \
sizeof(struct f2fs_xattr_entry))
#endif

View File

@ -28,6 +28,7 @@ typedef u32 block_t;
typedef u32 nid_t;
typedef u8 bool;
typedef unsigned long pgoff_t;
typedef unsigned short umode_t;
#if HAVE_BYTESWAP_H
#include <byteswap.h>
@ -208,6 +209,9 @@ static inline uint64_t bswap_64(uint64_t val)
#define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */
#define CHECKSUM_OFFSET 4092
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
#define F2FS_BLKSIZE_BITS 12
/* for mkfs */
#define F2FS_NUMBER_OF_CHECKPOINT_PACK 2
#define DEFAULT_SECTOR_SIZE 512
@ -222,6 +226,7 @@ enum f2fs_config_func {
DUMP,
DEFRAG,
RESIZE,
SLOAD,
};
struct f2fs_configuration {
@ -264,6 +269,10 @@ struct f2fs_configuration {
u_int64_t defrag_start;
u_int64_t defrag_len;
u_int64_t defrag_target;
/* sload parameters */
char *from_dir;
char *mount_point;
} __attribute__((packed));
#ifdef CONFIG_64BIT
@ -529,7 +538,9 @@ struct f2fs_extent {
#define F2FS_NAME_LEN 255
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
#define ADDRS_PER_INODE(fi) addrs_per_inode(fi)
#define ADDRS_PER_INODE(i) addrs_per_inode(i)
#define DEF_ADDRS_PER_INODE_INLINE_XATTR \
(DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS)
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
@ -545,8 +556,8 @@ struct f2fs_extent {
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
F2FS_INLINE_XATTR_ADDRS - 1))
#define MAX_INLINE_DATA (sizeof(__le32) * \
(DEF_ADDRS_PER_INODE_INLINE_XATTR - 1))
#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \
- sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1))
@ -634,6 +645,7 @@ struct f2fs_node {
* For NAT entries
*/
#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry))
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
struct f2fs_nat_entry {
__u8 version; /* latest version of cached nat entry */

View File

@ -1,3 +1,3 @@
## Makefile.am
dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8

View File

@ -72,4 +72,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
.BR mkfs.f2fs(8),
.BR dump.f2fs(8),
.BR fsck.f2fs(8),
.BR resize.f2fs(8).
.BR resize.f2fs(8),
.BR sload.f2fs(8).

View File

@ -66,4 +66,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
.BR mkfs.f2fs(8),
.BR fsck.f2fs(8),
.BR defrag.f2fs(8),
.BR resize.f2fs(8).
.BR resize.f2fs(8),
.BR sload.f2fs(8).

View File

@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
.BR mkfs.f2fs(8),
.BR dump.f2fs(8),
.BR defrag.f2fs(8),
.BR resize.f2fs(8).
.BR resize.f2fs(8),
.BR sload.f2fs(8).

View File

@ -95,4 +95,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
.BR fsck.f2fs(8),
.BR dump.f2fs(8),
.BR defrag.f2fs(8),
.BR resize.f2fs(8).
.BR resize.f2fs(8),
.BR sload.f2fs(8).

View File

@ -46,4 +46,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
.BR mkfs.f2fs(8),
.BR fsck.f2fs(8),
.BR dump.f2fs(8),
.BR defrag.f2fs(8).
.BR defrag.f2fs(8),
.BR sload.f2fs(8).

56
man/sload.f2fs.8 Normal file
View File

@ -0,0 +1,56 @@
.\" Copyright (C) 2015 Huawei Ltd.
.\"
.TH SLOAD.F2FS 8
.SH NAME
sload.f2fs \- load directories and files into the device directly
.SH SYNOPSIS
.B sload.f2fs
[
.B \-f
.I source directory path
]
[
.B \-t
.I mount point
]
[
.B \-d
.I debugging-level
]
.I device
.SH DESCRIPTION
.B sload.f2fs
is used to load directories and files into a disk partition.
\fIdevice\fP is the special file corresponding to the device (e.g.
\fI/dev/sdXX\fP).
.PP
The exit code returned by
.B sload.f2fs
is 0 on success and -1 on failure.
.SH OPTIONS
.TP
.BI \-f " source directory path"
Specify the source directory path to be loaded.
.TP
.BI \-t " mount point path"
Specify the mount point path in the partition to load.
.TP
.BI \-d " debug-level"
Specify the level of debugging options.
The default number is 0, which shows basic debugging messages.
.TP
.SH AUTHOR
This version of
.B sload.f2fs
has been written by Hou Pengyang <houpengyang@huawei.com>,
Liu Shuoran <liushuoran@huawei.com>, Jaegeuk Kim <jaegeuk@kernel.org>
.SH AVAILABILITY
.B sload.f2fs
is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
.SH SEE ALSO
.BR mkfs.f2fs(8),
.BR fsck.f2fs(8),
.BR dump.f2fs(8),
.BR defrag.f2fs(8),
.BR resize.f2fs(8).