sload.f2fs: support loading files into partition directly
authorJaegeuk Kim <jaegeuk@kernel.org>
Thu, 10 Dec 2015 00:18:44 +0000 (16:18 -0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Tue, 19 Apr 2016 01:11:26 +0000 (21:11 -0400)
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>
23 files changed:
README
configure.ac
fsck/Makefile.am
fsck/defrag.c
fsck/dir.c [new file with mode: 0644]
fsck/f2fs.h
fsck/fsck.h
fsck/main.c
fsck/mount.c
fsck/node.c [new file with mode: 0644]
fsck/node.h [new file with mode: 0644]
fsck/segment.c [new file with mode: 0644]
fsck/sload.c [new file with mode: 0644]
fsck/xattr.c [new file with mode: 0644]
fsck/xattr.h [new file with mode: 0644]
include/f2fs_fs.h
man/Makefile.am
man/defrag.f2fs.8
man/dump.f2fs.8
man/fsck.f2fs.8
man/mkfs.f2fs.8
man/resize.f2fs.8
man/sload.f2fs.8 [new file with mode: 0644]

diff --git a/README b/README
index 222cbc3..77d9b49 100644 (file)
--- a/README
+++ b/README
@@ -12,6 +12,7 @@ Your should install the following packages.
  - pkg-config
  - autoconf
  - libtool
+ - libselinux1-dev
 
 Initial compilation
 -------------------
index 1f6ff5a..42df775 100644 (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 \
index 3586625..7abcd00 100644 (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
index 7abc0bf..079e7a7 100644 (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",
diff --git a/fsck/dir.c b/fsck/dir.c
new file mode 100644 (file)
index 0000000..18b79bd
--- /dev/null
@@ -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;
+}
index 4b3c666..39ff161 100644 (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)
index db11b41..b0481d1 100644 (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_ */
index 885e2cf..cf670ed 100644 (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);
index 67c681e..4bde179 100644 (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);
 
diff --git a/fsck/node.c b/fsck/node.c
new file mode 100644 (file)
index 0000000..c2f83b8
--- /dev/null
@@ -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];
+}
diff --git a/fsck/node.h b/fsck/node.h
new file mode 100644 (file)
index 0000000..721e5b7
--- /dev/null
@@ -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
diff --git a/fsck/segment.c b/fsck/segment.c
new file mode 100644 (file)
index 0000000..9ce8bf5
--- /dev/null
@@ -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;
+}
diff --git a/fsck/sload.c b/fsck/sload.c
new file mode 100644 (file)
index 0000000..424e642
--- /dev/null
@@ -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;
+}
diff --git a/fsck/xattr.c b/fsck/xattr.c
new file mode 100644 (file)
index 0000000..3f5c7d3
--- /dev/null
@@ -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);
+}
diff --git a/fsck/xattr.h b/fsck/xattr.h
new file mode 100644 (file)
index 0000000..3ca9133
--- /dev/null
@@ -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
index 99798d0..1cc08fd 100644 (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 */
index 6c04de7..7856586 100644 (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
index 8c709a7..b08399b 100644 (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).
index dc1d806..35616e5 100644 (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).
index 22457c5..af1076c 100644 (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).
index b767c0c..0e54fe8 100644 (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).
index 1920810..463eca5 100644 (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).
diff --git a/man/sload.f2fs.8 b/man/sload.f2fs.8
new file mode 100644 (file)
index 0000000..d07330c
--- /dev/null
@@ -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).