[automerger skipped] Merge changes from topic "discard" into oc-dev
am: d16862a25e -s ours
Change-Id: I8a57695bc2963dfbb393c0ed1640e32935a7e233
diff --git a/.gitignore b/.gitignore
index 76473a6..debeffb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,7 @@
stamp-h1
/mkfs/mkfs.f2fs
+/fsck/fsck.f2fs
+/tools/f2fstat
+/tools/fibmap.f2fs
+/tools/parse.f2fs
diff --git a/Android.mk b/Android.mk
index d0f4e76..afd719b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,7 +4,11 @@
ifeq ($(HOST_OS),linux)
# The versions depend on $(LOCAL_PATH)/VERSION
-version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=4 -DF2FS_TOOLS_VERSION=\"1.4.0\" -DF2FS_TOOLS_DATE=\"2014-10-18\" -DWITH_BLKDISCARD
+version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=8 -DF2FS_TOOLS_VERSION=\"1.8.0\" -DF2FS_TOOLS_DATE=\"2017-02-03\"
+common_CFLAGS := -DWITH_ANDROID $(version_CFLAGS)
+# Workaround for the <sys/types.h>/<sys/sysmacros.h> split, here now for
+# bionic and coming later for glibc.
+target_CFLAGS := $(common_CFLAGS) -include sys/sysmacros.h
# external/e2fsprogs/lib is needed for uuid/uuid.h
common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/
@@ -18,7 +22,7 @@
mkfs/f2fs_format_utils.c \
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
include $(BUILD_STATIC_LIBRARY)
@@ -31,7 +35,7 @@
mkfs/f2fs_format_utils.c \
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -40,10 +44,12 @@
LOCAL_MODULE := libf2fs_fmt_host_dyn
LOCAL_SRC_FILES := \
lib/libf2fs.c \
+ lib/libf2fs_io.c \
mkfs/f2fs_format.c \
+ mkfs/f2fs_format_utils.c \
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
LOCAL_STATIC_LIBRARIES := \
libf2fs_ioutils_host \
@@ -67,7 +73,7 @@
lib/libf2fs_io.c \
mkfs/f2fs_format_main.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
LOCAL_STATIC_LIBRARIES := libc libf2fs_fmt libext2_uuid
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
@@ -80,7 +86,7 @@
lib/libf2fs_io.c \
mkfs/f2fs_format_main.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
LOCAL_STATIC_LIBRARIES := libf2fs_fmt
LOCAL_SHARED_LIBRARIES := libext2_uuid
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
@@ -100,7 +106,7 @@
lib/libf2fs_io.c \
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(target_CFLAGS)
LOCAL_SHARED_LIBRARIES := libext2_uuid
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
LOCAL_MODULE_TAGS := optional
@@ -118,7 +124,7 @@
lib/libf2fs_io.c \
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(version_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_HOST_SHARED_LIBRARIES := libext2_uuid
include $(BUILD_HOST_EXECUTABLE)
diff --git a/README b/README
index 222cbc3..4ea3356 100644
--- a/README
+++ b/README
@@ -7,24 +7,26 @@
Before compilation
------------------
-Your should install the following packages.
+You should install the following packages.
- libuuid-devel or uuid-dev
- pkg-config
- autoconf
- libtool
+ - libselinux1-dev
Initial compilation
-------------------
Before compilation initially, autoconf/automake tools should be run.
- # autoreconf --install
+ # ./autogen.sh
How to compile
--------------
# ./configure
# make
+ # make install
How to cross-compile (e.g., for ARM)
------------------------------------
@@ -43,6 +45,6 @@
How to run by default
---------------------
- $ ./mkfs.f2fs -l [LABEL] $DEV
+ $ mkfs.f2fs -l [LABEL] $DEV
For more mkfs options, see man page.
diff --git a/VERSION b/VERSION
index 0e58c09..c520af0 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-1.4.1
-2015-03-04
+1.8.0
+2017-02-03
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..2b0945d
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+aclocal && \
+autoheader && \
+autoconf && \
+libtoolize && \
+automake -a -c
diff --git a/configure.ac b/configure.ac
index ae451b8..6a3f7c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,12 @@
sys/mount.h
])
+# Test configure options.
+AC_ARG_WITH([selinux],
+ AS_HELP_STRING([--without-selinux],
+ [Ignore presence of libselinux and disable selinux support])
+)
+
# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL
@@ -55,8 +61,21 @@
# Checks for libraries.
PKG_CHECK_MODULES([libuuid], [uuid])
+AS_IF([test "x$with_selinux" != "xno"],
+ [PKG_CHECK_MODULES([libselinux], [libselinux],
+ [have_selinux=yes], [have_selinux=no])],
+ [have_selinux=no]
+)
+
+AS_IF([test "x$have_selinux" = "xyes"],
+ [AC_DEFINE([HAVE_LIBSELINUX], [1], [Use libselinux])],
+ [AS_IF([test "x$with_selinux" = "xyes"],
+ [AC_MSG_ERROR([selinux support requested but libselinux not found])]
+ )]
+)
+
# Checks for header files.
-AC_CHECK_HEADERS([linux/fs.h fcntl.h mntent.h stdlib.h string.h \
+AC_CHECK_HEADERS([linux/fs.h linux/blkzoned.h fcntl.h mntent.h stdlib.h string.h \
sys/ioctl.h sys/mount.h unistd.h linux/falloc.h byteswap.h])
# Checks for typedefs, structures, and compiler characteristics.
@@ -68,6 +87,7 @@
# Checks for library functions.
AC_FUNC_GETMNTENT
AC_CHECK_FUNCS_ONCE([
+ fallocate
getmntent
memset
])
@@ -76,10 +96,10 @@
[AC_CHECK_DECLS([bswap_64],,,[#include <byteswap.h>])])
# Install directories
-AC_PREFIX_DEFAULT([/usr])
-AC_SUBST([sbindir], [/sbin])
-AC_SUBST([sysconfdir], [/etc])
-AC_SUBST([localstatedir], [/var])
+#AC_PREFIX_DEFAULT([/usr])
+#AC_SUBST([sbindir], [/sbin])
+#AC_SUBST([sysconfdir], [/etc])
+#AC_SUBST([localstatedir], [/var])
AC_CONFIG_FILES([
Makefile
man/Makefile
@@ -89,4 +109,14 @@
tools/Makefile
])
+# export library version info for mkfs/libf2fs_format_la
+AC_SUBST(FMT_CURRENT, 1)
+AC_SUBST(FMT_REVISION, 0)
+AC_SUBST(FMT_AGE, 0)
+
+# export library version info for lib/libf2fs_la
+AC_SUBST(LIBF2FS_CURRENT, 2)
+AC_SUBST(LIBF2FS_REVISION, 0)
+AC_SUBST(LIBF2FS_AGE, 0)
+
AC_OUTPUT
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 3258e47..7abcd00 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -3,8 +3,13 @@
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
-fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+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 \
+ 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
diff --git a/fsck/defrag.c b/fsck/defrag.c
new file mode 100644
index 0000000..bea0293
--- /dev/null
+++ b/fsck/defrag.c
@@ -0,0 +1,102 @@
+/**
+ * defrag.c
+ *
+ * Copyright (c) 2015 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"
+
+static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
+{
+ void *raw = calloc(BLOCK_SZ, 1);
+ struct seg_entry *se;
+ struct f2fs_summary sum;
+ u64 offset;
+ int ret, type;
+
+ ASSERT(raw != NULL);
+
+ /* read from */
+ ret = dev_read_block(raw, from);
+ ASSERT(ret >= 0);
+
+ /* write to */
+ ret = dev_write_block(raw, to);
+ ASSERT(ret >= 0);
+
+ /* update sit bitmap & valid_blocks && se->type */
+ se = get_seg_entry(sbi, GET_SEGNO(sbi, from));
+ offset = OFFSET_IN_SEG(sbi, from);
+ type = se->type;
+ se->valid_blocks--;
+ f2fs_clear_bit(offset, (char *)se->cur_valid_map);
+ se->dirty = 1;
+
+ se = get_seg_entry(sbi, GET_SEGNO(sbi, to));
+ offset = OFFSET_IN_SEG(sbi, to);
+ se->type = type;
+ se->valid_blocks++;
+ f2fs_set_bit(offset, (char *)se->cur_valid_map);
+ se->dirty = 1;
+
+ /* read/write SSA */
+ get_sum_entry(sbi, from, &sum);
+ update_sum_entry(sbi, to, &sum);
+
+ /* if data block, read node and update node block */
+ if (IS_DATASEG(type))
+ update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
+ le16_to_cpu(sum.ofs_in_node), to);
+ else
+ update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
+
+ DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
+ IS_DATASEG(type) ? "data" : "node",
+ from, to);
+ free(raw);
+ return 0;
+}
+
+int f2fs_defragment(struct f2fs_sb_info *sbi, u64 from, u64 len, u64 to, int left)
+{
+ struct seg_entry *se;
+ u64 idx, offset;
+
+ /* flush NAT/SIT journal entries */
+ flush_journal_entries(sbi);
+
+ for (idx = from; idx < from + len; idx++) {
+ u64 target = to;
+
+ se = get_seg_entry(sbi, GET_SEGNO(sbi, idx));
+ offset = OFFSET_IN_SEG(sbi, idx);
+
+ if (!f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+ continue;
+
+ if (find_next_free_block(sbi, &target, left, se->type)) {
+ MSG(0, "Not enough space to migrate blocks");
+ return -1;
+ }
+
+ if (migrate_block(sbi, idx, target)) {
+ ASSERT_MSG("Found inconsistency: please run FSCK");
+ return -1;
+ }
+ }
+
+ /* update curseg info; can update sit->types */
+ move_curseg_info(sbi, to);
+ 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/dir.c b/fsck/dir.c
new file mode 100644
index 0000000..d817d27
--- /dev/null
+++ b/fsck/dir.c
@@ -0,0 +1,597 @@
+/**
+ * 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 = le32_to_cpu(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;
+
+ if (dir->i.i_inline & F2FS_INLINE_DENTRY) {
+ ERR_MSG("Not support to find \"%s\" in inline_dir pino=%x\n",
+ de->name, de->pino);
+ return 0;
+ }
+
+ max_depth = le32_to_cpu(dir->i.i_current_depth);
+ for (level = 0; level < max_depth; level ++) {
+ if (find_in_level(sbi, dir, level, de))
+ return 1;
+ }
+ return 0;
+}
+
+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 = cpu_to_le16(len);
+ de->hash_code = name_hash;
+ memcpy(d->filename[bit_pos], name, len);
+ d->filename[bit_pos][len] = 0;
+ de->ino = cpu_to_le32(ino);
+ 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;
+ }
+ if (parent->i.i_inline & F2FS_INLINE_DENTRY) {
+ ERR_MSG("Not support adding \"%s\" in inline_dir pino=%x\n",
+ de->name, de->pino);
+ 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;
+}
diff --git a/fsck/dump.c b/fsck/dump.c
index 3c4a8d1..8cbeda1 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -15,57 +15,177 @@
#define BUF_SZ 80
-const char *seg_type_name[SEG_TYPE_MAX] = {
+const char *seg_type_name[SEG_TYPE_MAX + 1] = {
"SEG_TYPE_DATA",
"SEG_TYPE_CUR_DATA",
"SEG_TYPE_NODE",
"SEG_TYPE_CUR_NODE",
+ "SEG_TYPE_NONE",
};
-void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit)
+void nat_dump(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ struct f2fs_nat_block *nat_block;
+ struct f2fs_node *node_block;
+ u32 nr_nat_blks, nid;
+ pgoff_t block_off;
+ pgoff_t block_addr;
+ char buf[BUF_SZ];
+ int seg_off;
+ int fd, ret, pack;
+ unsigned int i;
+
+ nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+ node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+ ASSERT(nat_block);
+
+ nr_nat_blks = get_sb(segment_count_nat) <<
+ (sbi->log_blocks_per_seg - 1);
+
+ fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666);
+ ASSERT(fd >= 0);
+
+ for (block_off = 0; block_off < nr_nat_blks; pack = 1, block_off++) {
+
+ 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;
+ pack = 2;
+ }
+
+ ret = dev_read_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+
+ nid = block_off * NAT_ENTRY_PER_BLOCK;
+ for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
+ struct f2fs_nat_entry raw_nat;
+ struct node_info ni;
+ ni.nid = nid + i;
+
+ if(nid + i == 0 || nid + i == 1 || nid + i == 2 )
+ continue;
+ if (lookup_nat_in_journal(sbi, nid + i,
+ &raw_nat) >= 0) {
+ node_info_from_raw_nat(&ni, &raw_nat);
+ ret = dev_read_block(node_block, ni.blk_addr);
+ ASSERT(ret >= 0);
+ if (ni.blk_addr != 0x0) {
+ memset(buf, 0, BUF_SZ);
+ snprintf(buf, BUF_SZ,
+ "nid:%5u\tino:%5u\toffset:%5u"
+ "\tblkaddr:%10u\tpack:%d\n",
+ ni.nid, ni.ino,
+ le32_to_cpu(node_block->footer.flag) >>
+ OFFSET_BIT_SHIFT,
+ ni.blk_addr, pack);
+ ret = write(fd, buf, strlen(buf));
+ ASSERT(ret >= 0);
+ }
+ } else {
+ node_info_from_raw_nat(&ni,
+ &nat_block->entries[i]);
+ if (ni.blk_addr == 0)
+ continue;
+
+ ret = dev_read_block(node_block, ni.blk_addr);
+ ASSERT(ret >= 0);
+ memset(buf, 0, BUF_SZ);
+ snprintf(buf, BUF_SZ,
+ "nid:%5u\tino:%5u\toffset:%5u"
+ "\tblkaddr:%10u\tpack:%d\n",
+ ni.nid, ni.ino,
+ le32_to_cpu(node_block->footer.flag) >>
+ OFFSET_BIT_SHIFT,
+ ni.blk_addr, pack);
+ ret = write(fd, buf, strlen(buf));
+ ASSERT(ret >= 0);
+ }
+ }
+ }
+
+ free(nat_block);
+ free(node_block);
+
+ close(fd);
+}
+
+void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit,
+ unsigned int end_sit)
{
struct seg_entry *se;
- int segno;
+ struct sit_info *sit_i = SIT_I(sbi);
+ unsigned int segno;
char buf[BUF_SZ];
u32 free_segs = 0;;
u64 valid_blocks = 0;
int ret;
- int fd;
+ int fd, i;
+ unsigned int offset;
fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
ASSERT(fd >= 0);
+ snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, "
+ "3:HN, 4:WN, 5:CN)\n");
+ ret = write(fd, buf, strlen(buf));
+ ASSERT(ret >= 0);
+
for (segno = start_sit; segno < end_sit; segno++) {
se = get_seg_entry(sbi, segno);
-
+ offset = SIT_BLOCK_OFFSET(sit_i, segno);
memset(buf, 0, BUF_SZ);
- snprintf(buf, BUF_SZ, "%5d %8d\n", segno, se->valid_blocks);
+ snprintf(buf, BUF_SZ,
+ "\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n",
+ segno, se->valid_blocks, se->type,
+ f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
- DBG(4, "SIT[0x%3x] : 0x%x\n", segno, se->valid_blocks);
if (se->valid_blocks == 0x0) {
free_segs++;
- } else {
- ASSERT(se->valid_blocks <= 512);
- valid_blocks += se->valid_blocks;
+ continue;
+ }
+
+ ASSERT(se->valid_blocks <= 512);
+ valid_blocks += se->valid_blocks;
+
+ for (i = 0; i < 64; i++) {
+ memset(buf, 0, BUF_SZ);
+ snprintf(buf, BUF_SZ, " %02x",
+ *(se->cur_valid_map + i));
+ ret = write(fd, buf, strlen(buf));
+ ASSERT(ret >= 0);
+
+ if ((i + 1) % 16 == 0) {
+ snprintf(buf, BUF_SZ, "\n");
+ ret = write(fd, buf, strlen(buf));
+ ASSERT(ret >= 0);
+ }
}
}
memset(buf, 0, BUF_SZ);
- snprintf(buf, BUF_SZ, "valid_segs:%d\t free_segs:%d\n",
- SM_I(sbi)->main_segments - free_segs, free_segs);
+ snprintf(buf, BUF_SZ,
+ "valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n",
+ valid_blocks,
+ SM_I(sbi)->main_segments - free_segs,
+ free_segs);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
close(fd);
- DBG(1, "Blocks [0x%" PRIx64 "] Free Segs [0x%x]\n", valid_blocks, free_segs);
}
void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
{
- struct f2fs_summary_block sum_blk;
+ struct f2fs_summary_block *sum_blk;
char buf[BUF_SZ];
int segno, i, ret;
int fd;
@@ -80,7 +200,7 @@
ASSERT(ret >= 0);
for (segno = start_ssa; segno < end_ssa; segno++) {
- ret = get_sum_block(sbi, segno, &sum_blk);
+ sum_blk = get_sum_block(sbi, segno, &ret);
memset(buf, 0, BUF_SZ);
switch (ret) {
@@ -108,15 +228,18 @@
ASSERT(ret >= 0);
}
snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
- le32_to_cpu(sum_blk.entries[i].nid));
+ le32_to_cpu(sum_blk->entries[i].nid));
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
+ if (ret == SEG_TYPE_NODE || ret == SEG_TYPE_DATA ||
+ ret == SEG_TYPE_MAX)
+ free(sum_blk);
}
close(fd);
}
-static void dump_data_blk(__u64 offset, u32 blkaddr)
+static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
{
char buf[F2FS_BLKSIZE];
@@ -124,7 +247,7 @@
return;
/* get data */
- if (blkaddr == NEW_ADDR) {
+ if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) {
memset(buf, 0, F2FS_BLKSIZE);
} else {
int ret;
@@ -171,7 +294,7 @@
for (i = 0; i < idx; i++, (*ofs)++) {
switch (ntype) {
case TYPE_DIRECT_NODE:
- dump_data_blk(*ofs * F2FS_BLKSIZE,
+ dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
le32_to_cpu(node_blk->dn.addr[i]));
break;
case TYPE_INDIRECT_NODE:
@@ -205,69 +328,74 @@
/* check data blocks in inode */
for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
- dump_data_blk(ofs * F2FS_BLKSIZE,
+ dump_data_blk(sbi, ofs * F2FS_BLKSIZE,
le32_to_cpu(node_blk->i.i_addr[i]));
/* check node blocks in inode */
for (i = 0; i < 5; i++) {
if (i == 0 || i == 1)
dump_node_blk(sbi, TYPE_DIRECT_NODE,
- node_blk->i.i_nid[i], &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
else if (i == 2 || i == 3)
dump_node_blk(sbi, TYPE_INDIRECT_NODE,
- node_blk->i.i_nid[i], &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
else if (i == 4)
dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
- node_blk->i.i_nid[i], &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
else
ASSERT(0);
}
}
-void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
- struct f2fs_node *node_blk)
+static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
+ struct f2fs_node *node_blk, int force)
{
struct f2fs_inode *inode = &node_blk->i;
u32 imode = le32_to_cpu(inode->i_mode);
- char name[255] = {0};
+ u32 namelen = le32_to_cpu(inode->i_namelen);
+ unsigned char name[F2FS_NAME_LEN + 1] = {0};
char path[1024] = {0};
char ans[255] = {0};
+ int is_encrypt = file_is_encrypt(inode);
int ret;
- if (!S_ISREG(imode)) {
- MSG(0, "Not a regular file\n\n");
+ if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
+ MSG(force, "Not a regular file or wrong name info\n\n");
return;
}
+ if (force)
+ goto dump;
printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
ret = scanf("%s", ans);
ASSERT(ret >= 0);
if (!strcasecmp(ans, "y")) {
+dump:
ret = system("mkdir -p ./lost_found");
ASSERT(ret >= 0);
/* make a file */
- strncpy(name, (const char *)inode->i_name,
- le32_to_cpu(inode->i_namelen));
- name[le32_to_cpu(inode->i_namelen)] = 0;
+ namelen = convert_encrypted_name(inode->i_name, namelen,
+ name, is_encrypt);
+ name[namelen] = 0;
sprintf(path, "./lost_found/%s", name);
- config.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
- ASSERT(config.dump_fd >= 0);
+ c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
+ ASSERT(c.dump_fd >= 0);
/* dump file's data */
dump_inode_blk(sbi, ni->ino, node_blk);
/* adjust file size */
- ret = ftruncate(config.dump_fd, le32_to_cpu(inode->i_size));
+ ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size));
ASSERT(ret >= 0);
- close(config.dump_fd);
+ close(c.dump_fd);
}
}
-void dump_node(struct f2fs_sb_info *sbi, nid_t nid)
+void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
{
struct node_info ni;
struct f2fs_node *node_blk;
@@ -283,17 +411,19 @@
DBG(1, "nat_entry.ino [0x%x]\n", ni.ino);
if (ni.blk_addr == 0x0)
- MSG(0, "Invalid nat entry\n\n");
+ MSG(force, "Invalid nat entry\n\n");
DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
- le32_to_cpu(node_blk->footer.nid) == ni.nid) {
- print_node_info(node_blk);
- dump_file(sbi, &ni, node_blk);
+ le32_to_cpu(node_blk->footer.nid) == ni.nid &&
+ ni.ino == ni.nid) {
+ print_node_info(node_blk, force);
+ dump_file(sbi, &ni, node_blk, force);
} else {
- MSG(0, "Invalid node block\n\n");
+ print_node_info(node_blk, force);
+ MSG(force, "Invalid (i)node block\n\n");
}
free(node_blk);
@@ -310,8 +440,8 @@
ret = dev_read_block(node_blk, blk_addr);
ASSERT(ret >= 0);
- if (config.dbg_lv > 0)
- print_node_info(node_blk);
+ if (c.dbg_lv > 0)
+ print_node_info(node_blk, 0);
else
print_inode_info(&node_blk->i, 1);
@@ -418,7 +548,9 @@
DBG(1, " - Segno [0x%x]\n", GET_SEGNO(sbi, blk_addr));
DBG(1, " - Offset [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
DBG(1, "SUM.nid [0x%x]\n", nid);
- DBG(1, "SUM.type [%s]\n", seg_type_name[type]);
+ DBG(1, "SUM.type [%s]\n", type >= 0 ?
+ seg_type_name[type] :
+ "Broken");
DBG(1, "SUM.version [%d]\n", sum_entry.version);
DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node);
DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr);
@@ -434,7 +566,7 @@
}
/* print inode */
- if (config.dbg_lv > 0)
+ if (c.dbg_lv > 0)
dump_node_from_blkaddr(ino_ni.blk_addr);
if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index c268f15..5c8eea5 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -19,8 +19,6 @@
#include <string.h>
#include <errno.h>
#include <mntent.h>
-#include <linux/types.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -60,6 +58,7 @@
char *nat_bitmap;
int bitmap_size;
+ char *nid_bitmap;
};
struct seg_entry {
@@ -74,6 +73,7 @@
unsigned char type; /* segment type like CURSEG_XXX_TYPE */
unsigned char orig_type; /* segment type like CURSEG_XXX_TYPE */
unsigned long long mtime; /* modification time of the segment */
+ int dirty;
};
struct sec_entry {
@@ -123,6 +123,44 @@
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;
@@ -159,7 +197,7 @@
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)
@@ -240,19 +278,10 @@
static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
{
- block_t start_addr;
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
- unsigned long long ckpt_version = le64_to_cpu(ckpt->checkpoint_ver);
+ block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
- start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
-
- /*
- * odd numbered checkpoint should at cp segment 0
- * and even segent must be at cp segment 1
- */
- if (!(ckpt_version & 1))
+ if (sbi->cur_cp == 2)
start_addr += sbi->blocks_per_seg;
-
return start_addr;
}
@@ -295,7 +324,7 @@
#define GET_R2L_SEGNO(sbi, segno) (segno + FREE_I_START_SEGNO(sbi))
#define START_BLOCK(sbi, segno) (SM_I(sbi)->main_blkaddr + \
- (segno << sbi->log_blocks_per_seg))
+ ((segno) << sbi->log_blocks_per_seg))
static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type)
{
@@ -313,25 +342,24 @@
- (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))
-#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats))
-#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits))
-
-#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne)
-#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid)
-#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se)
-#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno)
+#define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne)
+#define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid)
+#define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se)
+#define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno)
#define SIT_ENTRY_OFFSET(sit_i, segno) \
- (segno % sit_i->sents_per_block)
+ ((segno) % sit_i->sents_per_block)
#define SIT_BLOCK_OFFSET(sit_i, segno) \
- (segno / SIT_ENTRY_PER_BLOCK)
+ ((segno) / SIT_ENTRY_PER_BLOCK)
#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
{
return (nid <= (NAT_ENTRY_PER_BLOCK *
- F2FS_RAW_SUPER(sbi)->segment_count_nat
+ le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count_nat)
<< (sbi->log_blocks_per_seg - 1)));
}
@@ -339,9 +367,9 @@
{
int i;
- if (addr >= F2FS_RAW_SUPER(sbi)->block_count ||
+ if (addr >= le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count) ||
addr < SM_I(sbi)->main_blkaddr) {
- ASSERT_MSG("block addr [0x%x]\n", addr);
+ DBG(1, "block addr [0x%x]\n", addr);
return 0;
}
@@ -355,6 +383,22 @@
return 1;
}
+static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno, int type)
+{
+ int i;
+
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+ if (type == i)
+ continue;
+
+ if (segno == curseg->segno)
+ return 1;
+ }
+ return 0;
+}
+
static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr)
{
ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr);
@@ -381,7 +425,44 @@
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)
#endif /* _F2FS_H_ */
diff --git a/fsck/fsck.c b/fsck/fsck.c
index 1b27ae0..e97ee0a 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -18,21 +18,19 @@
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct seg_entry *se;
+ int fix = 0;
se = get_seg_entry(sbi, GET_SEGNO(sbi, blk));
- if (se->type != type) {
- if (type == CURSEG_WARM_DATA) {
- if (se->type != CURSEG_COLD_DATA) {
- DBG(1, "Wrong segment type [0x%x] %x -> %x",
- GET_SEGNO(sbi, blk), se->type,
- CURSEG_WARM_DATA);
- se->type = CURSEG_WARM_DATA;
- }
- } else {
- DBG(1, "Wrong segment type [0x%x] %x -> %x",
+ if (se->type >= NO_CHECK_TYPE)
+ fix = 1;
+ else if (IS_DATASEG(se->type) != IS_DATASEG(type))
+ fix = 1;
+
+ /* just check data and node types */
+ if (fix) {
+ DBG(1, "Wrong segment type [0x%x] %x -> %x",
GET_SEGNO(sbi, blk), se->type, type);
- se->type = type;
- }
+ se->type = type;
}
return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap);
}
@@ -63,6 +61,7 @@
node->nid = nid;
node->links = link_cnt;
+ node->actual_links = 1;
node->next = NULL;
if (fsck->hard_link_list_head == NULL) {
@@ -112,6 +111,7 @@
/* Decrease link count */
node->links = node->links - 1;
+ node->actual_links++;
/* if link count becomes one, remove the node */
if (node->links == 1) {
@@ -127,72 +127,227 @@
static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
u32 blk_addr)
{
+ struct f2fs_summary_block *sum_blk;
+ struct f2fs_summary *sum_entry;
+ struct seg_entry * se;
+ u32 segno, offset;
+ int need_fix = 0, ret = 0;
+ int type;
+
+ segno = GET_SEGNO(sbi, blk_addr);
+ offset = OFFSET_IN_SEG(sbi, blk_addr);
+
+ sum_blk = get_sum_block(sbi, segno, &type);
+
+ if (type != SEG_TYPE_NODE && type != SEG_TYPE_CUR_NODE) {
+ /* can't fix current summary, then drop the block */
+ if (!c.fix_on || type < 0) {
+ ASSERT_MSG("Summary footer is not for node segment");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ need_fix = 1;
+ se = get_seg_entry(sbi, segno);
+ if(IS_NODESEG(se->type)) {
+ FIX_MSG("Summary footer indicates a node segment: 0x%x", segno);
+ sum_blk->footer.entry_type = SUM_TYPE_NODE;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ sum_entry = &(sum_blk->entries[offset]);
+
+ if (le32_to_cpu(sum_entry->nid) != nid) {
+ if (!c.fix_on || type < 0) {
+ DBG(0, "nid [0x%x]\n", nid);
+ DBG(0, "target blk_addr [0x%x]\n", blk_addr);
+ DBG(0, "summary blk_addr [0x%x]\n",
+ GET_SUM_BLKADDR(sbi,
+ GET_SEGNO(sbi, blk_addr)));
+ DBG(0, "seg no / offset [0x%x / 0x%x]\n",
+ GET_SEGNO(sbi, blk_addr),
+ OFFSET_IN_SEG(sbi, blk_addr));
+ DBG(0, "summary_entry.nid [0x%x]\n",
+ le32_to_cpu(sum_entry->nid));
+ DBG(0, "--> node block's nid [0x%x]\n", nid);
+ ASSERT_MSG("Invalid node seg summary\n");
+ ret = -EINVAL;
+ } else {
+ FIX_MSG("Set node summary 0x%x -> [0x%x] [0x%x]",
+ segno, nid, blk_addr);
+ sum_entry->nid = cpu_to_le32(nid);
+ need_fix = 1;
+ }
+ }
+ if (need_fix && !c.ro) {
+ u64 ssa_blk;
+ int ret2;
+
+ ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+ ret2 = dev_write_block(sum_blk, ssa_blk);
+ ASSERT(ret2 >= 0);
+ }
+out:
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+ return ret;
+}
+
+static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
+ u32 blk_addr)
+{
+ u16 ofs_in_node = le16_to_cpu(sum->ofs_in_node);
+ u32 nid = le32_to_cpu(sum->nid);
+ struct f2fs_node *node_blk = NULL;
+ __le32 target_blk_addr;
+ struct node_info ni;
int ret = 0;
- struct f2fs_summary sum_entry;
- ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+ node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk != NULL);
- if (ret != SEG_TYPE_NODE && ret != SEG_TYPE_CUR_NODE) {
- ASSERT_MSG("Summary footer is not for node segment");
- return -EINVAL;
- }
+ if (!IS_VALID_NID(sbi, nid))
+ goto out;
- if (le32_to_cpu(sum_entry.nid) != nid) {
- DBG(0, "nid [0x%x]\n", nid);
- DBG(0, "target blk_addr [0x%x]\n", blk_addr);
- DBG(0, "summary blk_addr [0x%x]\n",
- GET_SUM_BLKADDR(sbi,
- GET_SEGNO(sbi, blk_addr)));
- DBG(0, "seg no / offset [0x%x / 0x%x]\n",
- GET_SEGNO(sbi, blk_addr),
- OFFSET_IN_SEG(sbi, blk_addr));
- DBG(0, "summary_entry.nid [0x%x]\n",
- le32_to_cpu(sum_entry.nid));
- DBG(0, "--> node block's nid [0x%x]\n", nid);
- ASSERT_MSG("Invalid node seg summary\n");
- return -EINVAL;
- }
- return 0;
+ get_node_info(sbi, nid, &ni);
+
+ if (!IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+ goto out;
+
+ /* read node_block */
+ ret = dev_read_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ if (le32_to_cpu(node_blk->footer.nid) != nid)
+ goto out;
+
+ /* check its block address */
+ if (node_blk->footer.nid == node_blk->footer.ino)
+ target_blk_addr = node_blk->i.i_addr[ofs_in_node];
+ else
+ target_blk_addr = node_blk->dn.addr[ofs_in_node];
+
+ if (blk_addr == le32_to_cpu(target_blk_addr))
+ ret = 1;
+out:
+ free(node_blk);
+ return ret;
}
static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
u32 parent_nid, u16 idx_in_node, u8 version)
{
- int ret = 0;
- struct f2fs_summary sum_entry;
+ struct f2fs_summary_block *sum_blk;
+ struct f2fs_summary *sum_entry;
+ struct seg_entry * se;
+ u32 segno, offset;
+ int need_fix = 0, ret = 0;
+ int type;
- ret = get_sum_entry(sbi, blk_addr, &sum_entry);
+ segno = GET_SEGNO(sbi, blk_addr);
+ offset = OFFSET_IN_SEG(sbi, blk_addr);
- if (ret != SEG_TYPE_DATA && ret != SEG_TYPE_CUR_DATA) {
- ASSERT_MSG("Summary footer is not for data segment");
- return -EINVAL;
+ sum_blk = get_sum_block(sbi, segno, &type);
+
+ if (type != SEG_TYPE_DATA && type != SEG_TYPE_CUR_DATA) {
+ /* can't fix current summary, then drop the block */
+ if (!c.fix_on || type < 0) {
+ ASSERT_MSG("Summary footer is not for data segment");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ need_fix = 1;
+ se = get_seg_entry(sbi, segno);
+ if (IS_DATASEG(se->type)) {
+ FIX_MSG("Summary footer indicates a data segment: 0x%x", segno);
+ sum_blk->footer.entry_type = SUM_TYPE_DATA;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
}
- if (le32_to_cpu(sum_entry.nid) != parent_nid ||
- sum_entry.version != version ||
- le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) {
+ sum_entry = &(sum_blk->entries[offset]);
- DBG(0, "summary_entry.nid [0x%x]\n",
- le32_to_cpu(sum_entry.nid));
- DBG(0, "summary_entry.version [0x%x]\n",
- sum_entry.version);
- DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
- le16_to_cpu(sum_entry.ofs_in_node));
- DBG(0, "parent nid [0x%x]\n", parent_nid);
- DBG(0, "version from nat [0x%x]\n", version);
- DBG(0, "idx in parent node [0x%x]\n", idx_in_node);
+ if (le32_to_cpu(sum_entry->nid) != parent_nid ||
+ sum_entry->version != version ||
+ le16_to_cpu(sum_entry->ofs_in_node) != idx_in_node) {
+ if (!c.fix_on || type < 0) {
+ DBG(0, "summary_entry.nid [0x%x]\n",
+ le32_to_cpu(sum_entry->nid));
+ DBG(0, "summary_entry.version [0x%x]\n",
+ sum_entry->version);
+ DBG(0, "summary_entry.ofs_in_node [0x%x]\n",
+ le16_to_cpu(sum_entry->ofs_in_node));
+ DBG(0, "parent nid [0x%x]\n",
+ parent_nid);
+ DBG(0, "version from nat [0x%x]\n", version);
+ DBG(0, "idx in parent node [0x%x]\n",
+ idx_in_node);
- DBG(0, "Target data block addr [0x%x]\n", blk_addr);
- ASSERT_MSG("Invalid data seg summary\n");
- return -EINVAL;
+ DBG(0, "Target data block addr [0x%x]\n", blk_addr);
+ ASSERT_MSG("Invalid data seg summary\n");
+ ret = -EINVAL;
+ } else if (is_valid_summary(sbi, sum_entry, blk_addr)) {
+ /* delete wrong index */
+ ret = -EINVAL;
+ } else {
+ FIX_MSG("Set data summary 0x%x -> [0x%x] [0x%x] [0x%x]",
+ segno, parent_nid, version, idx_in_node);
+ sum_entry->nid = cpu_to_le32(parent_nid);
+ sum_entry->version = version;
+ sum_entry->ofs_in_node = cpu_to_le16(idx_in_node);
+ need_fix = 1;
+ }
}
+ if (need_fix && !c.ro) {
+ u64 ssa_blk;
+ int ret2;
+
+ ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+ ret2 = dev_write_block(sum_blk, ssa_blk);
+ ASSERT(ret2 >= 0);
+ }
+out:
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+ return ret;
+}
+
+static int __check_inode_mode(u32 nid, enum FILE_TYPE ftype, u32 mode)
+{
+ if (ftype >= F2FS_FT_MAX)
+ return 0;
+ if (S_ISLNK(mode) && ftype != F2FS_FT_SYMLINK)
+ goto err;
+ if (S_ISREG(mode) && ftype != F2FS_FT_REG_FILE)
+ goto err;
+ if (S_ISDIR(mode) && ftype != F2FS_FT_DIR)
+ goto err;
+ if (S_ISCHR(mode) && ftype != F2FS_FT_CHRDEV)
+ goto err;
+ if (S_ISBLK(mode) && ftype != F2FS_FT_BLKDEV)
+ goto err;
+ if (S_ISFIFO(mode) && ftype != F2FS_FT_FIFO)
+ goto err;
+ if (S_ISSOCK(mode) && ftype != F2FS_FT_SOCK)
+ goto err;
return 0;
+err:
+ ASSERT_MSG("mismatch i_mode [0x%x] [0x%x vs. 0x%x]", nid, ftype, mode);
+ return -1;
}
static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
struct f2fs_node *node_blk,
enum FILE_TYPE ftype, enum NODE_TYPE ntype,
- struct node_info *ni)
+ struct node_info *ni, u8 *name)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
int ret;
@@ -203,18 +358,18 @@
}
get_node_info(sbi, nid, ni);
+ if (ni->ino == 0) {
+ ASSERT_MSG("nid[0x%x] ino is 0", nid);
+ return -EINVAL;
+ }
+
if (ni->blk_addr == NEW_ADDR) {
ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid);
return -EINVAL;
}
if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) {
- ASSERT_MSG("blkaddres is not valid. [0x%x]", ni->blk_addr);
- return -EINVAL;
- }
-
- if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) {
- ASSERT_MSG("summary node block is not valid. [0x%x]", nid);
+ ASSERT_MSG("blkaddress is not valid. [0x%x]", ni->blk_addr);
return -EINVAL;
}
@@ -228,6 +383,11 @@
le32_to_cpu(node_blk->footer.ino));
return -EINVAL;
}
+ if (ni->ino != le32_to_cpu(node_blk->footer.ino)) {
+ ASSERT_MSG("nid[0x%x] nat_entry->ino[0x%x] footer.ino[0x%x]",
+ nid, ni->ino, le32_to_cpu(node_blk->footer.ino));
+ return -EINVAL;
+ }
if (ntype != TYPE_INODE &&
node_blk->footer.nid == node_blk->footer.ino) {
ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]",
@@ -263,6 +423,14 @@
}
}
+ /* this if only from fix_hard_links */
+ if (ftype == F2FS_FT_MAX)
+ return 0;
+
+ if (ntype == TYPE_INODE &&
+ __check_inode_mode(nid, ftype, le32_to_cpu(node_blk->i.i_mode)))
+ return -EINVAL;
+
/* workaround to fix later */
if (ftype != F2FS_FT_ORPHAN ||
f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0)
@@ -271,6 +439,11 @@
ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n",
nid);
+ if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) {
+ ASSERT_MSG("summary node block is not valid. [0x%x]", nid);
+ return -EINVAL;
+ }
+
if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0)
ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]",
ni->blk_addr);
@@ -297,7 +470,7 @@
/* Sanity check */
if (sanity_check_nid(sbi, x_nid, node_blk,
- F2FS_FT_XATTR, TYPE_XATTR, &ni)) {
+ F2FS_FT_XATTR, TYPE_XATTR, &ni, NULL)) {
ret = -EINVAL;
goto out;
}
@@ -311,8 +484,8 @@
}
int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
- u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
- u32 *blk_cnt)
+ u32 nid, u8 *name, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+ u32 *blk_cnt, struct child_info *child)
{
struct node_info ni;
struct f2fs_node *node_blk = NULL;
@@ -320,7 +493,7 @@
node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
ASSERT(node_blk != NULL);
- if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni))
+ if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni, name))
goto err;
if (ntype == TYPE_INODE) {
@@ -331,19 +504,19 @@
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_WARM_NODE);
fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk,
- blk_cnt, &ni);
+ blk_cnt, child, &ni);
break;
case TYPE_INDIRECT_NODE:
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_COLD_NODE);
fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
- blk_cnt);
+ blk_cnt, child);
break;
case TYPE_DOUBLE_INDIRECT_NODE:
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_COLD_NODE);
fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
- blk_cnt);
+ blk_cnt, child);
break;
default:
ASSERT(0);
@@ -356,20 +529,87 @@
return -EINVAL;
}
+static inline void get_extent_info(struct extent_info *ext,
+ struct f2fs_extent *i_ext)
+{
+ ext->fofs = le32_to_cpu(i_ext->fofs);
+ ext->blk = le32_to_cpu(i_ext->blk_addr);
+ ext->len = le32_to_cpu(i_ext->len);
+}
+
+static void check_extent_info(struct child_info *child,
+ block_t blkaddr, int last)
+{
+ struct extent_info *ei = &child->ei;
+ u32 pgofs = child->pgofs;
+ int is_hole = 0;
+
+ if (!ei->len)
+ return;
+
+ if (child->state & FSCK_UNMATCHED_EXTENT)
+ return;
+
+ if (last) {
+ /* hole exist in the back of extent */
+ if (child->last_blk != ei->blk + ei->len - 1)
+ child->state |= FSCK_UNMATCHED_EXTENT;
+ return;
+ }
+
+ if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR)
+ is_hole = 1;
+
+ if (pgofs >= ei->fofs && pgofs < ei->fofs + ei->len) {
+ /* unmatched blkaddr */
+ if (is_hole || (blkaddr != pgofs - ei->fofs + ei->blk))
+ goto unmatched;
+
+ if (!child->last_blk) {
+ /* hole exists in the front of extent */
+ if (pgofs != ei->fofs)
+ goto unmatched;
+ } else if (child->last_blk + 1 != blkaddr) {
+ /* hole exists in the middle of extent */
+ goto unmatched;
+ }
+ child->last_blk = blkaddr;
+ return;
+ }
+
+ if (is_hole)
+ return;
+
+ if (blkaddr < ei->blk || blkaddr >= ei->blk + ei->len)
+ return;
+ /* unmatched file offset */
+unmatched:
+ child->state |= FSCK_UNMATCHED_EXTENT;
+}
+
/* start with valid nid and blkaddr */
void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
enum FILE_TYPE ftype, struct f2fs_node *node_blk,
u32 *blk_cnt, struct node_info *ni)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
- u32 child_cnt = 0, child_files = 0;
+ struct child_info child;
enum NODE_TYPE ntype;
u32 i_links = le32_to_cpu(node_blk->i.i_links);
+ u64 i_size = le64_to_cpu(node_blk->i.i_size);
u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
+ unsigned char en[F2FS_NAME_LEN + 1];
+ int namelen;
unsigned int idx = 0;
int need_fix = 0;
int ret;
+ memset(&child, 0, sizeof(child));
+ child.links = 2;
+ child.p_ino = nid;
+ child.pp_ino = le32_to_cpu(node_blk->i.i_pino);
+ child.dir_level = node_blk->i.i_dir_level;
+
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0)
fsck->chk.valid_inode_cnt++;
@@ -379,7 +619,7 @@
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
f2fs_set_main_bitmap(sbi, ni->blk_addr,
CURSEG_WARM_NODE);
- if (i_links > 1) {
+ if (i_links > 1 && ftype != F2FS_FT_ORPHAN) {
/* First time. Create new hard link node */
add_into_hard_link_list(sbi, nid, i_links);
fsck->chk.multi_hard_link_files++;
@@ -389,7 +629,7 @@
if (find_and_dec_hard_link_list(sbi, nid)) {
ASSERT_MSG("[0x%x] needs more i_links=0x%x",
nid, i_links);
- if (config.fix_on) {
+ if (c.fix_on) {
node_blk->i.i_links =
cpu_to_le32(i_links + 1);
need_fix = 1;
@@ -397,7 +637,7 @@
"i_links= 0x%x -> 0x%x",
nid, i_links, i_links + 1);
}
- goto check;
+ goto skip_blkcnt_fix;
}
/* No need to go deep into the node */
return;
@@ -406,7 +646,7 @@
if (fsck_chk_xattr_blk(sbi, nid,
le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt) &&
- config.fix_on) {
+ c.fix_on) {
node_blk->i.i_xattr_nid = 0;
need_fix = 1;
FIX_MSG("Remove xattr block: 0x%x, x_nid = 0x%x",
@@ -442,8 +682,7 @@
}
if((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) {
DBG(3, "ino[0x%x] has inline dentry!\n", nid);
- ret = fsck_chk_inline_dentries(sbi, node_blk,
- &child_cnt, &child_files);
+ ret = fsck_chk_inline_dentries(sbi, node_blk, &child);
if (ret < 0) {
/* should fix this bug all the time */
need_fix = 1;
@@ -464,17 +703,27 @@
}
}
+ /* init extent info */
+ get_extent_info(&child.ei, &node_blk->i.i_ext);
+ child.last_blk = 0;
+
/* check data blocks in inode */
- for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) {
- if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) {
+ for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i);
+ idx++, child.pgofs++) {
+ block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[idx]);
+
+ /* check extent info */
+ check_extent_info(&child, blkaddr, 0);
+
+ if (blkaddr != 0) {
ret = fsck_chk_data_blk(sbi,
- le32_to_cpu(node_blk->i.i_addr[idx]),
- &child_cnt, &child_files,
- (i_blocks == *blk_cnt),
- ftype, nid, idx, ni->version);
+ blkaddr,
+ &child, (i_blocks == *blk_cnt),
+ ftype, nid, idx, ni->version,
+ file_is_encrypt(&node_blk->i));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
- } else if (config.fix_on) {
+ } else if (c.fix_on) {
node_blk->i.i_addr[idx] = 0;
need_fix = 1;
FIX_MSG("[0x%x] i_addr[%d] = 0", nid, idx);
@@ -484,6 +733,8 @@
/* check node blocks in inode */
for (idx = 0; idx < 5; idx++) {
+ nid_t i_nid = le32_to_cpu(node_blk->i.i_nid[idx]);
+
if (idx == 0 || idx == 1)
ntype = TYPE_DIRECT_NODE;
else if (idx == 2 || idx == 3)
@@ -493,58 +744,115 @@
else
ASSERT(0);
- if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) {
- ret = fsck_chk_node_blk(sbi, &node_blk->i,
- le32_to_cpu(node_blk->i.i_nid[idx]),
- ftype, ntype, blk_cnt);
- if (!ret) {
- *blk_cnt = *blk_cnt + 1;
- } else if (config.fix_on) {
+ if (i_nid == 0x0)
+ goto skip;
+
+ ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid,
+ NULL, ftype, ntype, blk_cnt, &child);
+ if (!ret) {
+ *blk_cnt = *blk_cnt + 1;
+ } else if (ret == -EINVAL) {
+ if (c.fix_on) {
node_blk->i.i_nid[idx] = 0;
need_fix = 1;
FIX_MSG("[0x%x] i_nid[%d] = 0", nid, idx);
}
+skip:
+ if (ntype == TYPE_DIRECT_NODE)
+ child.pgofs += ADDRS_PER_BLOCK;
+ else if (ntype == TYPE_INDIRECT_NODE)
+ child.pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+ else
+ child.pgofs += ADDRS_PER_BLOCK *
+ NIDS_PER_BLOCK * NIDS_PER_BLOCK;
}
+
+ }
+
+ /* check uncovered range in the back of extent */
+ check_extent_info(&child, 0, 1);
+
+ if (child.state & FSCK_UNMATCHED_EXTENT) {
+ ASSERT_MSG("ino: 0x%x has wrong ext: [pgofs:%u, blk:%u, len:%u]",
+ nid, child.ei.fofs, child.ei.blk, child.ei.len);
+ if (c.fix_on)
+ need_fix = 1;
}
check:
- if (ftype == F2FS_FT_DIR)
- DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
- le32_to_cpu(node_blk->footer.ino),
- node_blk->i.i_name,
- le32_to_cpu(node_blk->i.i_current_depth),
- child_files);
- if (ftype == F2FS_FT_ORPHAN)
- DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
- le32_to_cpu(node_blk->footer.ino),
- node_blk->i.i_name,
- (u32)i_blocks);
-
if (i_blocks != *blk_cnt) {
ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", "
"but has %u blocks",
nid, i_blocks, *blk_cnt);
- if (config.fix_on) {
+ if (c.fix_on) {
node_blk->i.i_blocks = cpu_to_le64(*blk_cnt);
need_fix = 1;
FIX_MSG("[0x%x] i_blocks=0x%08"PRIx64" -> 0x%x",
nid, i_blocks, *blk_cnt);
}
}
- if (ftype == F2FS_FT_DIR && i_links != child_cnt) {
- ASSERT_MSG("ino: 0x%x has i_links: %u but real links: %u",
- nid, i_links, child_cnt);
- if (config.fix_on) {
- node_blk->i.i_links = cpu_to_le32(child_cnt);
+skip_blkcnt_fix:
+ namelen = convert_encrypted_name(node_blk->i.i_name,
+ le32_to_cpu(node_blk->i.i_namelen),
+ en, file_is_encrypt(&node_blk->i));
+ en[namelen] = '\0';
+ if (ftype == F2FS_FT_ORPHAN)
+ DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
+ le32_to_cpu(node_blk->footer.ino),
+ en, (u32)i_blocks);
+
+ if (ftype == F2FS_FT_DIR) {
+ DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
+ le32_to_cpu(node_blk->footer.ino), en,
+ le32_to_cpu(node_blk->i.i_current_depth),
+ child.files);
+
+ if (i_links != child.links) {
+ ASSERT_MSG("ino: 0x%x i_links: %u, real links: %u",
+ nid, i_links, child.links);
+ if (c.fix_on) {
+ node_blk->i.i_links = cpu_to_le32(child.links);
+ need_fix = 1;
+ FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x",
+ nid, i_links, child.links);
+ }
+ }
+ if (child.dots < 2 &&
+ !(node_blk->i.i_inline & F2FS_INLINE_DOTS)) {
+ ASSERT_MSG("ino: 0x%x dots: %u",
+ nid, child.dots);
+ if (c.fix_on) {
+ node_blk->i.i_inline |= F2FS_INLINE_DOTS;
+ need_fix = 1;
+ FIX_MSG("Dir: 0x%x set inline_dots", nid);
+ }
+ }
+ }
+ if (ftype == F2FS_FT_SYMLINK && i_blocks && i_size == 0) {
+ DBG(1, "ino: 0x%x i_blocks: %lu with zero i_size",
+ nid, i_blocks);
+ if (c.fix_on) {
+ u64 i_size = i_blocks * F2FS_BLKSIZE;
+
+ node_blk->i.i_size = cpu_to_le64(i_size);
need_fix = 1;
- FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x",
- nid, i_links, child_cnt);
+ FIX_MSG("Symlink: recover 0x%x with i_size=%lu",
+ nid, i_size);
}
}
- if (ftype == F2FS_FT_ORPHAN && i_links)
- ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u",
+ if (ftype == F2FS_FT_ORPHAN && i_links) {
+ MSG(0, "ino: 0x%x is orphan inode, but has i_links: %u",
nid, i_links);
- if (need_fix) {
+ if (c.fix_on) {
+ node_blk->i.i_links = 0;
+ need_fix = 1;
+ FIX_MSG("ino: 0x%x orphan_inode, i_links= 0x%x -> 0",
+ nid, i_links);
+ }
+ }
+ if (need_fix && !c.ro) {
+ /* drop extent information to avoid potential wrong access */
+ node_blk->i.i_ext.len = 0;
ret = dev_write_block(node_blk, ni->blk_addr);
ASSERT(ret >= 0);
}
@@ -552,29 +860,34 @@
int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk,
- u32 *blk_cnt, struct node_info *ni)
+ u32 *blk_cnt, struct child_info *child, struct node_info *ni)
{
int idx, ret;
- u32 child_cnt = 0, child_files = 0;
int need_fix = 0;
+ child->p_ino = nid;
+ child->pp_ino = le32_to_cpu(inode->i_pino);
- for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) {
- if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0)
+ for (idx = 0; idx < ADDRS_PER_BLOCK; idx++, child->pgofs++) {
+ block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]);
+
+ check_extent_info(child, blkaddr, 0);
+
+ if (blkaddr == 0x0)
continue;
ret = fsck_chk_data_blk(sbi,
- le32_to_cpu(node_blk->dn.addr[idx]),
- &child_cnt, &child_files,
+ blkaddr, child,
le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
- nid, idx, ni->version);
+ nid, idx, ni->version,
+ file_is_encrypt(inode));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
- } else if (config.fix_on) {
+ } else if (c.fix_on) {
node_blk->dn.addr[idx] = 0;
need_fix = 1;
FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, idx);
}
}
- if (need_fix) {
+ if (need_fix && !c.ro) {
ret = dev_write_block(node_blk, ni->blk_addr);
ASSERT(ret >= 0);
}
@@ -582,69 +895,154 @@
}
int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
- enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
+ enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
+ struct child_info *child)
{
- int ret;
+ int need_fix = 0, ret;
int i = 0;
- for (i = 0 ; i < NIDS_PER_BLOCK; i++) {
+ for (i = 0; i < NIDS_PER_BLOCK; i++) {
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
- continue;
+ goto skip;
ret = fsck_chk_node_blk(sbi, inode,
- le32_to_cpu(node_blk->in.nid[i]),
- ftype, TYPE_DIRECT_NODE, blk_cnt);
+ le32_to_cpu(node_blk->in.nid[i]), NULL,
+ ftype, TYPE_DIRECT_NODE, blk_cnt, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
- else if (ret == -EINVAL)
- printf("delete in.nid[i] = 0;\n");
+ else if (ret == -EINVAL) {
+ if (!c.fix_on)
+ printf("should delete in.nid[i] = 0;\n");
+ else {
+ node_blk->in.nid[i] = 0;
+ need_fix = 1;
+ FIX_MSG("Set indirect node 0x%x -> 0\n", i);
+ }
+skip:
+ child->pgofs += ADDRS_PER_BLOCK;
+ }
}
+
+ if (need_fix && !c.ro) {
+ struct node_info ni;
+ nid_t nid = le32_to_cpu(node_blk->footer.nid);
+
+ get_node_info(sbi, nid, &ni);
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ }
+
return 0;
}
int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
- enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt)
+ enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
+ struct child_info *child)
{
int i = 0;
- int ret = 0;
+ int need_fix = 0, ret = 0;
for (i = 0; i < NIDS_PER_BLOCK; i++) {
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
- continue;
+ goto skip;
ret = fsck_chk_node_blk(sbi, inode,
- le32_to_cpu(node_blk->in.nid[i]),
- ftype, TYPE_INDIRECT_NODE, blk_cnt);
+ le32_to_cpu(node_blk->in.nid[i]), NULL,
+ ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
- else if (ret == -EINVAL)
- printf("delete in.nid[i] = 0;\n");
+ else if (ret == -EINVAL) {
+ if (!c.fix_on)
+ printf("should delete in.nid[i] = 0;\n");
+ else {
+ node_blk->in.nid[i] = 0;
+ need_fix = 1;
+ FIX_MSG("Set double indirect node 0x%x -> 0\n", i);
+ }
+skip:
+ child->pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+ }
}
+
+ if (need_fix && !c.ro) {
+ struct node_info ni;
+ nid_t nid = le32_to_cpu(node_blk->footer.nid);
+
+ get_node_info(sbi, nid, &ni);
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ }
+
return 0;
}
+static const char *lookup_table =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+/**
+ * digest_encode() -
+ *
+ * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ */
+static int digest_encode(const char *src, int len, char *dst)
+{
+ int i = 0, bits = 0, ac = 0;
+ char *cp = dst;
+
+ while (i < len) {
+ ac += (((unsigned char) src[i]) << bits);
+ bits += 8;
+ do {
+ *cp++ = lookup_table[ac & 0x3f];
+ ac >>= 6;
+ bits -= 6;
+ } while (bits >= 6);
+ i++;
+ }
+ if (bits)
+ *cp++ = lookup_table[ac & 0x3f];
+ *cp = 0;
+ return cp - dst;
+}
+
+int convert_encrypted_name(unsigned char *name, int len,
+ unsigned char *new, int encrypted)
+{
+ if (!encrypted) {
+ memcpy(new, name, len);
+ new[len] = 0;
+ return len;
+ }
+
+ *new = '_';
+ return digest_encode((const char *)name, 24, (char *)new + 1);
+}
+
static void print_dentry(__u32 depth, __u8 *name,
- unsigned long *bitmap,
- struct f2fs_dir_entry *dentry,
- int max, int idx, int last_blk)
+ u8 *bitmap, struct f2fs_dir_entry *dentry,
+ int max, int idx, int last_blk, int encrypted)
{
int last_de = 0;
int next_idx = 0;
int name_len;
unsigned int i;
int bit_offset;
+ unsigned char new[F2FS_NAME_LEN + 1];
- if (config.dbg_lv != -1)
+ if (c.dbg_lv != -1)
return;
name_len = le16_to_cpu(dentry[idx].name_len);
next_idx = idx + (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
- bit_offset = find_next_bit(bitmap, max, next_idx);
+ bit_offset = find_next_bit_le(bitmap, max, next_idx);
if (bit_offset >= max && last_blk)
last_de = 1;
if (tree_mark_size <= depth) {
tree_mark_size *= 2;
+ ASSERT(tree_mark_size != 0);
tree_mark = realloc(tree_mark, tree_mark_size);
+ ASSERT(tree_mark != NULL);
}
if (last_de)
@@ -655,144 +1053,333 @@
if (tree_mark[depth - 1] == '`')
tree_mark[depth - 1] = ' ';
-
for (i = 1; i < depth; i++)
printf("%c ", tree_mark[i]);
- printf("%c-- %s 0x%x\n", last_de ? '`' : '|',
- name, le32_to_cpu(dentry[idx].ino));
+
+ convert_encrypted_name(name, name_len, new, encrypted);
+
+ printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
+ last_de ? '`' : '|',
+ new, le32_to_cpu(dentry[idx].ino),
+ encrypted);
}
-static int __chk_dentries(struct f2fs_sb_info *sbi, u32 *child_cnt,
- u32* child_files,
- unsigned long *bitmap,
- struct f2fs_dir_entry *dentry,
+static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
+ const unsigned char *name, u32 len, int encrypted)
+{
+ f2fs_hash_t hash_code = f2fs_dentry_hash(name, len);
+
+ /* fix hash_code made by old buggy code */
+ if (dentry->hash_code != hash_code) {
+ unsigned char new[F2FS_NAME_LEN + 1];
+
+ convert_encrypted_name((unsigned char *)name, len,
+ new, encrypted);
+ FIX_MSG("Mismatch hash_code for \"%s\" [%x:%x]",
+ new, le32_to_cpu(dentry->hash_code),
+ hash_code);
+ dentry->hash_code = cpu_to_le32(hash_code);
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned int dir_buckets(unsigned int level, int dir_level)
+{
+ if (level + dir_level < MAX_DIR_HASH_DEPTH / 2)
+ return 1 << (level + dir_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 __get_current_level(int dir_level, u32 pgofs)
+{
+ unsigned int bidx = 0;
+ int i;
+
+ for (i = 0; i < MAX_DIR_HASH_DEPTH; i++) {
+ bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
+ if (bidx > pgofs)
+ break;
+ }
+ return i;
+}
+
+static int f2fs_check_dirent_position(u8 *name, u16 name_len, u32 pgofs,
+ u8 dir_level, u32 pino)
+{
+ f2fs_hash_t namehash = f2fs_dentry_hash(name, name_len);
+ unsigned int nbucket, nblock;
+ unsigned int bidx, end_block;
+ int level;
+
+ level = __get_current_level(dir_level, pgofs);
+
+ nbucket = dir_buckets(level, dir_level);
+ nblock = bucket_blocks(level);
+
+ bidx = dir_block_index(level, dir_level,
+ le32_to_cpu(namehash) % nbucket);
+ end_block = bidx + nblock;
+
+ if (pgofs >= bidx && pgofs < end_block)
+ return 0;
+
+ ASSERT_MSG("Wrong position of dirent pino:%u, name:%s, level:%d, "
+ "dir_level:%d, pgofs:%u, correct range:[%u, %u]\n",
+ pino, name, level, dir_level, pgofs, bidx, end_block - 1);
+ return 1;
+}
+
+static int __chk_dots_dentries(struct f2fs_sb_info *sbi,
+ struct f2fs_dir_entry *dentry,
+ struct child_info *child,
+ u8 *name, int len,
+ __u8 (*filename)[F2FS_SLOT_LEN],
+ int encrypted)
+{
+ int fixed = 0;
+
+ if ((name[0] == '.' && len == 1)) {
+ if (le32_to_cpu(dentry->ino) != child->p_ino) {
+ ASSERT_MSG("Bad inode number[0x%x] for '.', parent_ino is [0x%x]\n",
+ le32_to_cpu(dentry->ino), child->p_ino);
+ dentry->ino = cpu_to_le32(child->p_ino);
+ fixed = 1;
+ }
+ }
+
+ if (name[0] == '.' && name[1] == '.' && len == 2) {
+ if (child->p_ino == F2FS_ROOT_INO(sbi)) {
+ if (le32_to_cpu(dentry->ino) != F2FS_ROOT_INO(sbi)) {
+ ASSERT_MSG("Bad inode number[0x%x] for '..'\n",
+ le32_to_cpu(dentry->ino));
+ dentry->ino = cpu_to_le32(F2FS_ROOT_INO(sbi));
+ fixed = 1;
+ }
+ } else if (le32_to_cpu(dentry->ino) != child->pp_ino) {
+ ASSERT_MSG("Bad inode number[0x%x] for '..', parent parent ino is [0x%x]\n",
+ le32_to_cpu(dentry->ino), child->pp_ino);
+ dentry->ino = cpu_to_le32(child->pp_ino);
+ fixed = 1;
+ }
+ }
+
+ if (f2fs_check_hash_code(dentry, name, len, encrypted))
+ fixed = 1;
+
+ if (name[len] != '\0') {
+ ASSERT_MSG("'.' is not NULL terminated\n");
+ name[len] = '\0';
+ memcpy(*filename, name, len);
+ fixed = 1;
+ }
+ return fixed;
+}
+
+static void nullify_dentry(struct f2fs_dir_entry *dentry, int offs,
+ __u8 (*filename)[F2FS_SLOT_LEN], u8 **bitmap)
+{
+ memset(dentry, 0, sizeof(struct f2fs_dir_entry));
+ test_and_clear_bit_le(offs, *bitmap);
+ memset(*filename, 0, F2FS_SLOT_LEN);
+}
+
+static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
+ u8 *bitmap, struct f2fs_dir_entry *dentry,
__u8 (*filenames)[F2FS_SLOT_LEN],
- int max, int last_blk)
+ int max, int last_blk, int encrypted)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
enum FILE_TYPE ftype;
int dentries = 0;
u32 blk_cnt;
u8 *name;
- u32 hash_code, ino;
- u16 name_len;;
+ unsigned char en[F2FS_NAME_LEN + 1];
+ u16 name_len, en_len;
int ret = 0;
int fixed = 0;
- int i;
+ int i, slots;
/* readahead inode blocks */
- for (i = 0; i < max;) {
- if (test_bit(i, bitmap) == 0) {
- i++;
+ for (i = 0; i < max; i++) {
+ u32 ino;
+
+ if (test_bit_le(i, bitmap) == 0)
continue;
- }
+
ino = le32_to_cpu(dentry[i].ino);
if (IS_VALID_NID(sbi, ino)) {
struct node_info ni;
get_node_info(sbi, ino, &ni);
- if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+ if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
dev_reada_block(ni.blk_addr);
+ name_len = le16_to_cpu(dentry[i].name_len);
+ if (name_len > 0)
+ i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN - 1;
+ }
}
- name_len = le16_to_cpu(dentry[i].name_len);
- i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
}
for (i = 0; i < max;) {
- if (test_bit(i, bitmap) == 0) {
+ if (test_bit_le(i, bitmap) == 0) {
i++;
continue;
}
if (!IS_VALID_NID(sbi, le32_to_cpu(dentry[i].ino))) {
- DBG(1, "Bad dentry 0x%x with invalid NID/ino 0x%x",
- i, le32_to_cpu(dentry[i].ino));
- if (config.fix_on) {
+ ASSERT_MSG("Bad dentry 0x%x with invalid NID/ino 0x%x",
+ i, le32_to_cpu(dentry[i].ino));
+ if (c.fix_on) {
FIX_MSG("Clear bad dentry 0x%x with bad ino 0x%x",
i, le32_to_cpu(dentry[i].ino));
- clear_bit(i, bitmap);
- i++;
+ test_and_clear_bit_le(i, bitmap);
fixed = 1;
- continue;
}
+ i++;
+ continue;
}
+
ftype = dentry[i].file_type;
- if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE) && config.fix_on) {
- DBG(1, "Bad dentry 0x%x with unexpected ftype 0x%x",
- i, ftype);
- if (config.fix_on) {
+ if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE)) {
+ ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x",
+ le32_to_cpu(dentry[i].ino), ftype);
+ if (c.fix_on) {
FIX_MSG("Clear bad dentry 0x%x with bad ftype 0x%x",
i, ftype);
- clear_bit(i, bitmap);
- i++;
+ test_and_clear_bit_le(i, bitmap);
fixed = 1;
- continue;
}
+ i++;
+ continue;
}
+
name_len = le16_to_cpu(dentry[i].name_len);
+
+ if (name_len == 0 || name_len > F2FS_NAME_LEN) {
+ ASSERT_MSG("Bad dentry 0x%x with invalid name_len", i);
+ if (c.fix_on) {
+ FIX_MSG("Clear bad dentry 0x%x", i);
+ test_and_clear_bit_le(i, bitmap);
+ fixed = 1;
+ }
+ i++;
+ continue;
+ }
name = calloc(name_len + 1, 1);
memcpy(name, filenames[i], name_len);
- hash_code = f2fs_dentry_hash((const unsigned char *)name,
- name_len);
-
- /* fix hash_code made by old buggy code */
- if (le32_to_cpu(dentry[i].hash_code) != hash_code) {
- dentry[i].hash_code = hash_code;
- fixed = 1;
- FIX_MSG("hash_code[%d] of %s", i, name);
- }
+ slots = (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
/* Becareful. 'dentry.file_type' is not imode. */
if (ftype == F2FS_FT_DIR) {
- *child_cnt = *child_cnt + 1;
if ((name[0] == '.' && name_len == 1) ||
(name[0] == '.' && name[1] == '.' &&
name_len == 2)) {
+ ret = __chk_dots_dentries(sbi, &dentry[i],
+ child, name, name_len, &filenames[i],
+ encrypted);
+ switch (ret) {
+ case 1:
+ fixed = 1;
+ case 0:
+ child->dots++;
+ break;
+ }
+
+ if (child->dots > 2) {
+ ASSERT_MSG("More than one '.' or '..', should delete the extra one\n");
+ nullify_dentry(&dentry[i], i,
+ &filenames[i], &bitmap);
+ child->dots--;
+ fixed = 1;
+ }
+
i++;
free(name);
continue;
}
}
+ if (f2fs_check_hash_code(dentry + i, name, name_len, encrypted))
+ fixed = 1;
+
+ if (max == NR_DENTRY_IN_BLOCK) {
+ ret = f2fs_check_dirent_position(name, name_len,
+ child->pgofs,
+ child->dir_level, child->p_ino);
+ if (ret) {
+ if (c.fix_on) {
+ FIX_MSG("Clear bad dentry 0x%x", i);
+ test_and_clear_bit_le(i, bitmap);
+ fixed = 1;
+ }
+ i++;
+ free(name);
+ continue;
+ }
+ }
+
+ en_len = convert_encrypted_name(name, name_len, en, encrypted);
+ en[en_len] = '\0';
DBG(1, "[%3u]-[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n",
- fsck->dentry_depth, i, name, name_len,
+ fsck->dentry_depth, i, en, name_len,
le32_to_cpu(dentry[i].ino),
dentry[i].file_type);
print_dentry(fsck->dentry_depth, name, bitmap,
- dentry, max, i, last_blk);
+ dentry, max, i, last_blk, encrypted);
blk_cnt = 1;
ret = fsck_chk_node_blk(sbi,
- NULL, le32_to_cpu(dentry[i].ino),
- ftype, TYPE_INODE, &blk_cnt);
+ NULL, le32_to_cpu(dentry[i].ino), name,
+ ftype, TYPE_INODE, &blk_cnt, NULL);
- if (ret && config.fix_on) {
+ if (ret && c.fix_on) {
int j;
- int slots = (name_len + F2FS_SLOT_LEN - 1) /
- F2FS_SLOT_LEN;
+
for (j = 0; j < slots; j++)
- clear_bit(i + j, bitmap);
+ test_and_clear_bit_le(i + j, bitmap);
FIX_MSG("Unlink [0x%x] - %s len[0x%x], type[0x%x]",
le32_to_cpu(dentry[i].ino),
name, name_len,
dentry[i].file_type);
- i += slots;
- free(name);
fixed = 1;
- continue;
+ } else if (ret == 0) {
+ if (ftype == F2FS_FT_DIR)
+ child->links++;
+ dentries++;
+ child->files++;
}
- i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
- dentries++;
- *child_files = *child_files + 1;
+ i += slots;
free(name);
}
return fixed ? -1 : dentries;
}
int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
- struct f2fs_node *node_blk, u32 *child_cnt, u32 *child_files)
+ struct f2fs_node *node_blk, struct child_info *child)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct f2fs_inline_dentry *de_blk;
@@ -802,10 +1389,11 @@
ASSERT(de_blk != NULL);
fsck->dentry_depth++;
- dentries = __chk_dentries(sbi, child_cnt, child_files,
- (unsigned long *)de_blk->dentry_bitmap,
+ dentries = __chk_dentries(sbi, child,
+ de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
- NR_INLINE_DENTRY, 1);
+ NR_INLINE_DENTRY, 1,
+ file_is_encrypt(&node_blk->i));
if (dentries < 0) {
DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
fsck->dentry_depth);
@@ -820,7 +1408,7 @@
}
int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
- u32 *child_cnt, u32 *child_files, int last_blk)
+ struct child_info *child, int last_blk, int encrypted)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct f2fs_dentry_block *de_blk;
@@ -833,12 +1421,12 @@
ASSERT(ret >= 0);
fsck->dentry_depth++;
- dentries = __chk_dentries(sbi, child_cnt, child_files,
- (unsigned long *)de_blk->dentry_bitmap,
+ dentries = __chk_dentries(sbi, child,
+ de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
- NR_DENTRY_IN_BLOCK, last_blk);
+ NR_DENTRY_IN_BLOCK, last_blk, encrypted);
- if (dentries < 0) {
+ if (dentries < 0 && !c.ro) {
ret = dev_write_block(de_blk, blk_addr);
ASSERT(ret >= 0);
DBG(1, "[%3d] Dentry Block [0x%x] Fixed hash_codes\n\n",
@@ -855,8 +1443,9 @@
}
int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
- u32 *child_cnt, u32 *child_files, int last_blk,
- enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver)
+ struct child_info *child, int last_blk,
+ enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver,
+ int encrypted)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -867,7 +1456,7 @@
}
if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) {
- ASSERT_MSG("blkaddres is not valid. [0x%x]", blk_addr);
+ ASSERT_MSG("blkaddress is not valid. [0x%x]", blk_addr);
return -EINVAL;
}
@@ -885,54 +1474,195 @@
ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]",
blk_addr, parent_nid, idx_in_node);
-
fsck->chk.valid_blk_cnt++;
if (ftype == F2FS_FT_DIR) {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA);
- return fsck_chk_dentry_blk(sbi, blk_addr, child_cnt,
- child_files, last_blk);
+ return fsck_chk_dentry_blk(sbi, blk_addr, child,
+ last_blk, encrypted);
} else {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA);
}
return 0;
}
-void fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
+int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
{
u32 blk_cnt = 0;
block_t start_blk, orphan_blkaddr, i, j;
- struct f2fs_orphan_block *orphan_blk;
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_orphan_block *orphan_blk, *new_blk;
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ u32 entry_count;
- if (!is_set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG))
- return;
+ if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG))
+ return 0;
- start_blk = __start_cp_addr(sbi) + 1 +
- le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
- orphan_blkaddr = __start_sum_addr(sbi) - 1;
+ start_blk = __start_cp_addr(sbi) + 1 + get_sb(cp_payload);
+ orphan_blkaddr = __start_sum_addr(sbi) - 1 - get_sb(cp_payload);
+
orphan_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(orphan_blk);
+
+ new_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(new_blk);
for (i = 0; i < orphan_blkaddr; i++) {
int ret = dev_read_block(orphan_blk, start_blk + i);
+ u32 new_entry_count = 0;
ASSERT(ret >= 0);
+ entry_count = le32_to_cpu(orphan_blk->entry_count);
- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
+ for (j = 0; j < entry_count; j++) {
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
DBG(1, "[%3d] ino [0x%x]\n", i, ino);
- if (config.fix_on) {
- FIX_MSG("Discard orphan inodes: ino [0x%x]",
- ino);
+ struct node_info ni;
+ blk_cnt = 1;
+
+ if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
+ get_node_info(sbi, ino, &ni);
+ if (!IS_VALID_NID(sbi, ino) ||
+ !IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+ return -EINVAL;
+
continue;
}
- blk_cnt = 1;
- fsck_chk_node_blk(sbi, NULL, ino,
- F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt);
+
+ ret = fsck_chk_node_blk(sbi, NULL, ino, NULL,
+ F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
+ NULL);
+ if (!ret)
+ new_blk->ino[new_entry_count++] =
+ orphan_blk->ino[j];
+ else if (ret && c.fix_on)
+ FIX_MSG("[0x%x] remove from orphan list", ino);
+ else if (ret)
+ ASSERT_MSG("[0x%x] wrong orphan inode", ino);
+ }
+ if (!c.ro && c.fix_on &&
+ entry_count != new_entry_count) {
+ new_blk->entry_count = cpu_to_le32(new_entry_count);
+ ret = dev_write_block(new_blk, start_blk + i);
+ ASSERT(ret >= 0);
}
memset(orphan_blk, 0, BLOCK_SZ);
+ memset(new_blk, 0, BLOCK_SZ);
}
free(orphan_blk);
+ free(new_blk);
+
+ return 0;
+}
+
+int fsck_chk_meta(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ struct seg_entry *se;
+ unsigned int sit_valid_segs = 0, sit_node_blks = 0;
+ unsigned int i;
+
+ /* 1. check sit usage with CP: curseg is lost? */
+ for (i = 0; i < TOTAL_SEGS(sbi); i++) {
+ se = get_seg_entry(sbi, i);
+ if (se->valid_blocks != 0)
+ sit_valid_segs++;
+ else if (IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE)) {
+ /* curseg has not been written back to device */
+ MSG(1, "\tInfo: curseg %u is counted in valid segs\n", i);
+ sit_valid_segs++;
+ }
+ if (IS_NODESEG(se->type))
+ sit_node_blks += se->valid_blocks;
+ }
+ if (fsck->chk.sit_free_segs + sit_valid_segs != TOTAL_SEGS(sbi)) {
+ ASSERT_MSG("SIT usage does not match: sit_free_segs %u, "
+ "sit_valid_segs %u, total_segs %u",
+ fsck->chk.sit_free_segs, sit_valid_segs,
+ TOTAL_SEGS(sbi));
+ return -EINVAL;
+ }
+
+ /* 2. check node count */
+ if (fsck->chk.valid_nat_entry_cnt != sit_node_blks) {
+ ASSERT_MSG("node count does not match: valid_nat_entry_cnt %u,"
+ " sit_node_blks %u",
+ fsck->chk.valid_nat_entry_cnt, sit_node_blks);
+ return -EINVAL;
+ }
+
+ /* 3. check SIT with CP */
+ if (fsck->chk.sit_free_segs != le32_to_cpu(cp->free_segment_count)) {
+ ASSERT_MSG("free segs does not match: sit_free_segs %u, "
+ "free_segment_count %u",
+ fsck->chk.sit_free_segs,
+ le32_to_cpu(cp->free_segment_count));
+ return -EINVAL;
+ }
+
+ /* 4. check NAT with CP */
+ if (fsck->chk.valid_nat_entry_cnt !=
+ le32_to_cpu(cp->valid_node_count)) {
+ ASSERT_MSG("valid node does not match: valid_nat_entry_cnt %u,"
+ " valid_node_count %u",
+ fsck->chk.valid_nat_entry_cnt,
+ le32_to_cpu(cp->valid_node_count));
+ return -EINVAL;
+ }
+
+ /* 4. check orphan inode simply */
+ if (fsck_chk_orphan_node(sbi))
+ return -EINVAL;
+
+ if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) {
+ ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u,"
+ " valid_inode_count %u",
+ fsck->nat_valid_inode_cnt,
+ le32_to_cpu(cp->valid_inode_count));
+ return -EINVAL;
+ }
+
+ /*check nat entry with sit_area_bitmap*/
+ for (i = 0; i < fsck->nr_nat_entries; i++) {
+ u32 blk = le32_to_cpu(fsck->entries[i].block_addr);
+ nid_t ino = le32_to_cpu(fsck->entries[i].ino);
+
+ if (!blk)
+ /*
+ * skip entry whose ino is 0, otherwise, we will
+ * get a negative number by BLKOFF_FROM_MAIN(sbi, blk)
+ */
+ continue;
+
+ if (!IS_VALID_BLK_ADDR(sbi, blk)) {
+ MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]"
+ " is in valid\n",
+ ino, blk);
+ return -EINVAL;
+ }
+
+ if (!f2fs_test_sit_bitmap(sbi, blk)) {
+ MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]"
+ " not find it in sit_area_bitmap\n",
+ ino, blk);
+ return -EINVAL;
+ }
+
+ if (!IS_VALID_NID(sbi, ino)) {
+ MSG(0, "\tError: nat_entry->ino %u exceeds the range"
+ " of nat entries %u\n",
+ ino, fsck->nr_nat_entries);
+ return -EINVAL;
+ }
+
+ if (!f2fs_test_bit(ino, fsck->nat_area_bitmap)) {
+ MSG(0, "\tError: nat_entry->ino %u is not set in"
+ " nat_area_bitmap\n", ino);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
}
void fsck_init(struct f2fs_sb_info *sbi)
@@ -958,10 +1688,46 @@
build_sit_area_bitmap(sbi);
+ ASSERT(tree_mark_size != 0);
tree_mark = calloc(tree_mark_size, 1);
ASSERT(tree_mark != NULL);
}
+static void fix_hard_links(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct hard_link_node *tmp, *node;
+ struct f2fs_node *node_blk = NULL;
+ struct node_info ni;
+ int ret;
+
+ if (fsck->hard_link_list_head == NULL)
+ return;
+
+ node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk != NULL);
+
+ node = fsck->hard_link_list_head;
+ while (node) {
+ /* Sanity check */
+ if (sanity_check_nid(sbi, node->nid, node_blk,
+ F2FS_FT_MAX, TYPE_INODE, &ni, NULL))
+ FIX_MSG("Failed to fix, rerun fsck.f2fs");
+
+ node_blk->i.i_links = cpu_to_le32(node->actual_links);
+
+ FIX_MSG("File: 0x%x i_links= 0x%x -> 0x%x",
+ node->nid, node->links, node->actual_links);
+
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ tmp = node;
+ node = node->next;
+ free(tmp);
+ }
+ free(node_blk);
+}
+
static void fix_nat_entries(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -972,44 +1738,73 @@
nullify_nat_entry(sbi, i);
}
+static void flush_curseg_sit_entries(struct f2fs_sb_info *sbi)
+{
+ struct sit_info *sit_i = SIT_I(sbi);
+ int i;
+
+ /* update curseg sit entries, since we may change
+ * a segment type in move_curseg_info
+ */
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ struct curseg_info *curseg = CURSEG_I(sbi, i);
+ struct f2fs_sit_block *sit_blk;
+ struct f2fs_sit_entry *sit;
+ struct seg_entry *se;
+
+ se = get_seg_entry(sbi, curseg->segno);
+ sit_blk = get_current_sit_page(sbi, curseg->segno);
+ sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, curseg->segno)];
+ sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+ se->valid_blocks);
+ rewrite_current_sit_page(sbi, curseg->segno, sit_blk);
+ free(sit_blk);
+ }
+}
+
static void fix_checkpoint(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
- struct f2fs_super_block *raw_sb = sbi->raw_super;
- struct f2fs_checkpoint *ckp = F2FS_CKPT(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
unsigned long long cp_blk_no;
+ u32 flags = CP_UMOUNT_FLAG;
+ block_t orphan_blks = 0;
u32 i;
int ret;
u_int32_t crc = 0;
- ckp->ckpt_flags = cpu_to_le32(CP_UMOUNT_FLAG);
- ckp->cp_pack_total_block_count =
- cpu_to_le32(8 + le32_to_cpu(raw_sb->cp_payload));
- ckp->cp_pack_start_sum = cpu_to_le32(1 +
- le32_to_cpu(raw_sb->cp_payload));
+ if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) {
+ orphan_blks = __start_sum_addr(sbi) - 1;
+ flags |= CP_ORPHAN_PRESENT_FLAG;
+ }
- ckp->free_segment_count = cpu_to_le32(fsck->chk.free_segs);
- ckp->valid_block_count = cpu_to_le32(fsck->chk.valid_blk_cnt);
- ckp->valid_node_count = cpu_to_le32(fsck->chk.valid_node_cnt);
- ckp->valid_inode_count = cpu_to_le32(fsck->chk.valid_inode_cnt);
+ set_cp(ckpt_flags, flags);
+ set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
- crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, ckp, CHECKSUM_OFFSET);
- *((__le32 *)((unsigned char *)ckp + CHECKSUM_OFFSET)) =
- cpu_to_le32(crc);
+ set_cp(free_segment_count, get_free_segments(sbi));
+ set_cp(valid_block_count, fsck->chk.valid_blk_cnt);
+ set_cp(valid_node_count, fsck->chk.valid_node_cnt);
+ set_cp(valid_inode_count, fsck->chk.valid_inode_cnt);
- cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+ crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
+ *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+ cp_blk_no = get_sb(cp_blkaddr);
if (sbi->cur_cp == 2)
- cp_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg);
+ cp_blk_no += 1 << get_sb(log_blocks_per_seg);
- ret = dev_write_block(ckp, cp_blk_no++);
+ ret = dev_write_block(cp, cp_blk_no++);
ASSERT(ret >= 0);
- for (i = 0; i < le32_to_cpu(raw_sb->cp_payload); i++) {
- ret = dev_write_block(((unsigned char *)ckp) + i * F2FS_BLKSIZE,
+ for (i = 0; i < get_sb(cp_payload); i++) {
+ ret = dev_write_block(((unsigned char *)cp) + i * F2FS_BLKSIZE,
cp_blk_no++);
ASSERT(ret >= 0);
}
+ cp_blk_no += orphan_blks;
+
for (i = 0; i < NO_CHECK_TYPE; i++) {
struct curseg_info *curseg = CURSEG_I(sbi, i);
@@ -1017,7 +1812,7 @@
ASSERT(ret >= 0);
}
- ret = dev_write_block(ckp, cp_blk_no++);
+ ret = dev_write_block(cp, cp_blk_no++);
ASSERT(ret >= 0);
}
@@ -1028,13 +1823,26 @@
for (i = 0; i < NO_CHECK_TYPE; i++) {
struct curseg_info *curseg = CURSEG_I(sbi, i);
struct seg_entry *se;
+ int j, nblocks;
+ if ((curseg->next_blkoff >> 3) >= SIT_VBLOCK_MAP_SIZE)
+ return -EINVAL;
se = get_seg_entry(sbi, curseg->segno);
if (f2fs_test_bit(curseg->next_blkoff,
- (const char *)se->cur_valid_map) == 1) {
+ (const char *)se->cur_valid_map)) {
ASSERT_MSG("Next block offset is not free, type:%d", i);
return -EINVAL;
}
+ if (curseg->alloc_type == SSR)
+ return 0;
+
+ nblocks = sbi->blocks_per_seg;
+ for (j = curseg->next_blkoff + 1; j < nblocks; j++) {
+ if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) {
+ ASSERT_MSG("LFS must have free section:%d", i);
+ return -EINVAL;
+ }
+ }
}
return 0;
}
@@ -1049,7 +1857,8 @@
se = get_seg_entry(sbi, i);
if (se->orig_type != se->type) {
- if (se->orig_type == CURSEG_COLD_DATA) {
+ if (se->orig_type == CURSEG_COLD_DATA &&
+ se->type <= CURSEG_COLD_DATA) {
se->type = se->orig_type;
} else {
FIX_MSG("Wrong segment type [0x%x] %x -> %x",
@@ -1086,7 +1895,7 @@
node->nid, node->links);
node = node->next;
}
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] Unreachable nat entries ");
@@ -1095,7 +1904,7 @@
} else {
printf(" [Fail] [0x%x]\n", nr_unref_nid);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] SIT valid block bitmap checking ");
@@ -1105,7 +1914,7 @@
} else {
printf("[Fail]\n");
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] Hard link checking for regular file ");
@@ -1114,7 +1923,7 @@
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] valid_block_count matching with CP ");
@@ -1123,7 +1932,7 @@
} else {
printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] valid_node_count matcing with CP (de lookup) ");
@@ -1132,7 +1941,7 @@
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] valid_node_count matcing with CP (nat lookup) ");
@@ -1141,7 +1950,7 @@
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] valid_inode_count matched with CP ");
@@ -1150,7 +1959,7 @@
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] free segment_count matched with CP ");
@@ -1160,7 +1969,7 @@
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs);
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] next block offset is free ");
@@ -1169,7 +1978,7 @@
} else {
printf(" [Fail]\n");
ret = EXIT_ERR_CODE;
- config.bug_on = 1;
+ c.bug_on = 1;
}
printf("[FSCK] fixing SIT types\n");
@@ -1177,18 +1986,43 @@
force = 1;
printf("[FSCK] other corrupted bugs ");
- if (config.bug_on == 0) {
+ if (c.bug_on == 0) {
printf(" [Ok..]\n");
} else {
printf(" [Fail]\n");
ret = EXIT_ERR_CODE;
}
+#ifndef WITH_ANDROID
+ if (nr_unref_nid && !c.ro) {
+ char ans[255] = {0};
+
+ printf("\nDo you want to restore lost files into ./lost_found/? [Y/N] ");
+ ret = scanf("%s", ans);
+ ASSERT(ret >= 0);
+ if (!strcasecmp(ans, "y")) {
+ for (i = 0; i < fsck->nr_nat_entries; i++) {
+ if (f2fs_test_bit(i, fsck->nat_area_bitmap))
+ dump_node(sbi, i, 1);
+ }
+ }
+ }
+#endif
/* fix global metadata */
- if (force || (config.bug_on && config.fix_on)) {
- fix_nat_entries(sbi);
- rewrite_sit_area_bitmap(sbi);
- fix_checkpoint(sbi);
+ if (force || (c.fix_on && !c.ro)) {
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+
+ if (force || c.bug_on) {
+ fix_hard_links(sbi);
+ fix_nat_entries(sbi);
+ rewrite_sit_area_bitmap(sbi);
+ move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+ write_curseg_info(sbi);
+ flush_curseg_sit_entries(sbi);
+ fix_checkpoint(sbi);
+ } else if (is_set_ckpt_flags(cp, CP_FSCK_FLAG)) {
+ write_checkpoint(sbi);
+ }
}
return ret;
}
@@ -1205,6 +2039,9 @@
if (fsck->sit_area_bitmap)
free(fsck->sit_area_bitmap);
+ if (fsck->entries)
+ free(fsck->entries);
+
if (tree_mark)
free(tree_mark);
}
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 9cad013..5a6f018 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -13,12 +13,47 @@
#include "f2fs.h"
+#define FSCK_UNMATCHED_EXTENT 0x00000001
+
+enum {
+ PREEN_MODE_0,
+ PREEN_MODE_1,
+ PREEN_MODE_MAX
+};
+
+enum {
+ NOERROR,
+ EWRONG_OPT,
+ ENEED_ARG,
+ EUNKNOWN_OPT,
+ EUNKNOWN_ARG,
+};
+
/* fsck.c */
struct orphan_info {
u32 nr_inodes;
u32 *ino_list;
};
+struct extent_info {
+ u32 fofs; /* start offset in a file */
+ u32 blk; /* start block address of the extent */
+ u32 len; /* length of the extent */
+};
+
+struct child_info {
+ u32 state;
+ u32 links;
+ u32 files;
+ u32 pgofs;
+ u8 dots;
+ u8 dir_level;
+ u32 p_ino; /*parent ino*/
+ u32 pp_ino; /*parent parent ino*/
+ struct extent_info ei;
+ u32 last_blk;
+};
+
struct f2fs_fsck {
struct f2fs_sb_info sbi;
@@ -31,7 +66,6 @@
u32 multi_hard_link_files;
u64 sit_valid_blocks;
u32 sit_free_segs;
- u32 free_segs;
} chk;
struct hard_link_node *hard_link_list_head;
@@ -49,6 +83,8 @@
u32 nr_nat_entries;
u32 dentry_depth;
+ struct f2fs_nat_entry *entries;
+ u32 nat_valid_inode_cnt;
};
#define BLOCK_SZ 4096
@@ -67,6 +103,7 @@
struct hard_link_node {
u32 nid;
u32 links;
+ u32 actual_links;
struct hard_link_node *next;
};
@@ -78,30 +115,40 @@
SEG_TYPE_MAX,
};
-extern void fsck_chk_orphan_node(struct f2fs_sb_info *);
+struct selabel_handle;
+
+extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
- enum FILE_TYPE, enum NODE_TYPE, u32 *);
+ u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
+ struct child_info *);
extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
struct f2fs_node *, u32 *, struct node_info *);
extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
u32, enum FILE_TYPE, struct f2fs_node *, u32 *,
- struct node_info *);
+ struct child_info *, struct node_info *);
extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
- enum FILE_TYPE, struct f2fs_node *, u32 *);
+ enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
- enum FILE_TYPE, struct f2fs_node *, u32 *);
-extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, u32 *, u32 *,
- int, enum FILE_TYPE, u32, u16, u8);
-extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, u32 *, u32 *, int);
+ enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, struct child_info *,
+ int, enum FILE_TYPE, u32, u16, u8, int);
+extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, struct child_info *,
+ int, int);
int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
- u32 *, u32 *);
+ struct child_info *);
+int fsck_chk_meta(struct f2fs_sb_info *sbi);
+int convert_encrypted_name(unsigned char *, int, unsigned char *, int);
-extern void print_node_info(struct f2fs_node *);
+extern void update_free_segments(struct f2fs_sb_info *);
+void print_cp_state(u32);
+extern void print_node_info(struct f2fs_node *, int);
extern void print_inode_info(struct f2fs_inode *, int);
extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int);
-extern int get_sum_block(struct f2fs_sb_info *, unsigned int,
- struct f2fs_summary_block *);
+extern struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *,
+ unsigned int, int *);
extern int get_sum_entry(struct f2fs_sb_info *, u32, struct f2fs_summary *);
+extern void update_sum_entry(struct f2fs_sb_info *, block_t,
+ struct f2fs_summary *);
extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
extern void nullify_nat_entry(struct f2fs_sb_info *, u32);
extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *);
@@ -113,9 +160,29 @@
extern int f2fs_do_mount(struct f2fs_sb_info *);
extern void f2fs_do_umount(struct f2fs_sb_info *);
+extern void flush_journal_entries(struct f2fs_sb_info *);
+extern void zero_journal_entries(struct f2fs_sb_info *);
+extern void flush_sit_entries(struct f2fs_sb_info *);
+extern void move_curseg_info(struct f2fs_sb_info *, u64);
+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, nid_t, block_t);
+
+extern void print_raw_sb_info(struct f2fs_super_block *);
+
+extern u32 get_free_segments(struct f2fs_sb_info *);
+extern struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *,
+ unsigned int);
+extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int,
+ struct f2fs_sit_block *);
+
/* dump.c */
struct dump_option {
nid_t nid;
+ int start_nat;
+ int end_nat;
int start_sit;
int end_sit;
int start_ssa;
@@ -123,9 +190,36 @@
int32_t blk_addr;
};
-extern void sit_dump(struct f2fs_sb_info *, int, int);
+extern void nat_dump(struct f2fs_sb_info *);
+extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int);
extern void ssa_dump(struct f2fs_sb_info *, int, int);
-extern void dump_node(struct f2fs_sb_info *, nid_t);
+extern void dump_node(struct f2fs_sb_info *, nid_t, int);
extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
+/* defrag.c */
+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_ */
diff --git a/fsck/main.c b/fsck/main.c
index 3606f62..6c94a70 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -3,6 +3,13 @@
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* 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
@@ -10,6 +17,7 @@
*/
#include "fsck.h"
#include <libgen.h>
+#include <ctype.h>
struct f2fs_fsck gfsck;
@@ -20,6 +28,7 @@
MSG(0, " -a check/fix potential corruption, reported by f2fs\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -f check/fix entire partition\n");
+ MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n");
MSG(0, " -t show directory tree [-d -1]\n");
exit(1);
}
@@ -30,6 +39,7 @@
MSG(0, "[options]:\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -i inode no (hex)\n");
+ MSG(0, " -n [NAT dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -b blk_addr (in 4KB)\n");
@@ -37,43 +47,153 @@
exit(1);
}
+void defrag_usage()
+{
+ MSG(0, "\nUsage: defrag.f2fs [options] device\n");
+ MSG(0, "[options]:\n");
+ MSG(0, " -d debug level [default:0]\n");
+ MSG(0, " -s start block address [default: main_blkaddr]\n");
+ MSG(0, " -l length [default:512 (2MB)]\n");
+ MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n");
+ MSG(0, " -i set direction as shrink [default: expand]\n");
+ exit(1);
+}
+
+void resize_usage()
+{
+ MSG(0, "\nUsage: resize.f2fs [options] device\n");
+ MSG(0, "[options]:\n");
+ MSG(0, " -d debug level [default:0]\n");
+ MSG(0, " -t target sectors [default: device size]\n");
+ 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);
+}
+
+static int is_digits(char *optarg)
+{
+ int i;
+
+ for (i = 0; i < strlen(optarg); i++)
+ if (!isdigit(optarg[i]))
+ break;
+ return i == strlen(optarg);
+}
+
+static void error_out(void)
+{
+ if (c.func == FSCK)
+ fsck_usage();
+ else if (c.func == DUMP)
+ dump_usage();
+ else if (c.func == DEFRAG)
+ defrag_usage();
+ else if (c.func == RESIZE)
+ resize_usage();
+ else if (c.func == SLOAD)
+ sload_usage();
+}
+
void f2fs_parse_options(int argc, char *argv[])
{
int option = 0;
char *prog = basename(argv[0]);
+ int err = NOERROR;
+
+ if (argc < 2) {
+ MSG(0, "\tError: Device not specified\n");
+ error_out();
+ }
+ c.devices[0].path = strdup(argv[argc - 1]);
+ argv[argc-- - 1] = 0;
if (!strcmp("fsck.f2fs", prog)) {
- const char *option_string = "ad:ft";
+ const char *option_string = ":ad:fp:t";
- config.func = FSCK;
+ c.func = FSCK;
while ((option = getopt(argc, argv, option_string)) != EOF) {
switch (option) {
case 'a':
- config.auto_fix = 1;
+ c.auto_fix = 1;
MSG(0, "Info: Fix the reported corruption.\n");
break;
+ case 'p':
+ /* preen mode has different levels:
+ * 0: default level, the same as -a
+ * 1: check meta
+ */
+ if (optarg[0] == '-') {
+ c.preen_mode = PREEN_MODE_0;
+ optind--;
+ break;
+ } else if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.preen_mode = atoi(optarg);
+ if (c.preen_mode < 0)
+ c.preen_mode = PREEN_MODE_0;
+ else if (c.preen_mode >= PREEN_MODE_MAX)
+ c.preen_mode = PREEN_MODE_MAX - 1;
+ if (c.preen_mode == PREEN_MODE_0)
+ c.auto_fix = 1;
+ MSG(0, "Info: Fix the reported corruption in "
+ "preen mode %d\n", c.preen_mode);
+ break;
case 'd':
- config.dbg_lv = atoi(optarg);
- MSG(0, "Info: Debug level = %d\n",
- config.dbg_lv);
+ if (optarg[0] == '-') {
+ err = ENEED_ARG;
+ break;
+ } else if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.dbg_lv = atoi(optarg);
+ MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
break;
case 'f':
- config.fix_on = 1;
+ c.fix_on = 1;
MSG(0, "Info: Force to fix corruption\n");
break;
case 't':
- config.dbg_lv = -1;
+ c.dbg_lv = -1;
break;
+
+
+ case ':':
+ if (optopt == 'p') {
+ MSG(0, "Info: Use default preen mode\n");
+ c.preen_mode = PREEN_MODE_0;
+ c.auto_fix = 1;
+ } else {
+ option = optopt;
+ err = ENEED_ARG;
+ break;
+ }
+ break;
+ case '?':
+ option = optopt;
default:
- MSG(0, "\tError: Unknown option %c\n", option);
- fsck_usage();
+ err = EUNKNOWN_OPT;
break;
}
+ if (err != NOERROR)
+ break;
}
} else if (!strcmp("dump.f2fs", prog)) {
- const char *option_string = "d:i:s:a:b:";
+ const char *option_string = "d:i:n:s:a:b:";
static struct dump_option dump_opt = {
- .nid = 3, /* default root ino */
+ .nid = 0, /* default root ino */
+ .start_nat = -1,
+ .end_nat = -1,
.start_sit = -1,
.end_sit = -1,
.start_ssa = -1,
@@ -81,15 +201,19 @@
.blk_addr = -1,
};
- config.func = DUMP;
+ c.func = DUMP;
while ((option = getopt(argc, argv, option_string)) != EOF) {
int ret = 0;
switch (option) {
case 'd':
- config.dbg_lv = atoi(optarg);
+ if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.dbg_lv = atoi(optarg);
MSG(0, "Info: Debug level = %d\n",
- config.dbg_lv);
+ c.dbg_lv);
break;
case 'i':
if (strncmp(optarg, "0x", 2))
@@ -99,6 +223,11 @@
ret = sscanf(optarg, "%x",
&dump_opt.nid);
break;
+ case 'n':
+ ret = sscanf(optarg, "%d~%d",
+ &dump_opt.start_nat,
+ &dump_opt.end_nat);
+ break;
case 's':
ret = sscanf(optarg, "%d~%d",
&dump_opt.start_sit,
@@ -118,83 +247,313 @@
&dump_opt.blk_addr);
break;
default:
- MSG(0, "\tError: Unknown option %c\n", option);
- dump_usage();
+ err = EUNKNOWN_OPT;
break;
}
ASSERT(ret >= 0);
+ if (err != NOERROR)
+ break;
}
- config.private = &dump_opt;
- }
+ c.private = &dump_opt;
+ } else if (!strcmp("defrag.f2fs", prog)) {
+ const char *option_string = "d:s:l:t:i";
- if ((optind + 1) != argc) {
- MSG(0, "\tError: Device not specified\n");
- if (config.func == FSCK)
- fsck_usage();
- else if (config.func == DUMP)
- dump_usage();
+ c.func = DEFRAG;
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ int ret = 0;
+
+ switch (option) {
+ case 'd':
+ if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.dbg_lv = atoi(optarg);
+ MSG(0, "Info: Debug level = %d\n",
+ c.dbg_lv);
+ break;
+ case 's':
+ if (strncmp(optarg, "0x", 2))
+ ret = sscanf(optarg, "%"PRIu64"",
+ &c.defrag_start);
+ else
+ ret = sscanf(optarg, "%"PRIx64"",
+ &c.defrag_start);
+ break;
+ case 'l':
+ if (strncmp(optarg, "0x", 2))
+ ret = sscanf(optarg, "%"PRIu64"",
+ &c.defrag_len);
+ else
+ ret = sscanf(optarg, "%"PRIx64"",
+ &c.defrag_len);
+ break;
+ case 't':
+ if (strncmp(optarg, "0x", 2))
+ ret = sscanf(optarg, "%"PRIu64"",
+ &c.defrag_target);
+ else
+ ret = sscanf(optarg, "%"PRIx64"",
+ &c.defrag_target);
+ break;
+ case 'i':
+ c.defrag_shrink = 1;
+ break;
+ default:
+ err = EUNKNOWN_OPT;
+ break;
+ }
+ ASSERT(ret >= 0);
+ if (err != NOERROR)
+ break;
+ }
+ } else if (!strcmp("resize.f2fs", prog)) {
+ const char *option_string = "d:t:";
+
+ c.func = RESIZE;
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ int ret = 0;
+
+ switch (option) {
+ case 'd':
+ if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.dbg_lv = atoi(optarg);
+ MSG(0, "Info: Debug level = %d\n",
+ c.dbg_lv);
+ break;
+ case 't':
+ if (strncmp(optarg, "0x", 2))
+ ret = sscanf(optarg, "%"PRIu64"",
+ &c.target_sectors);
+ else
+ ret = sscanf(optarg, "%"PRIx64"",
+ &c.target_sectors);
+ break;
+ default:
+ err = EUNKNOWN_OPT;
+ break;
+ }
+ ASSERT(ret >= 0);
+ if (err != NOERROR)
+ break;
+ }
+ } else if (!strcmp("sload.f2fs", prog)) {
+ const char *option_string = "d:f:t:";
+
+ c.func = SLOAD;
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ switch (option) {
+ case 'd':
+ if (!is_digits(optarg)) {
+ err = EWRONG_OPT;
+ break;
+ }
+ c.dbg_lv = atoi(optarg);
+ MSG(0, "Info: Debug level = %d\n",
+ c.dbg_lv);
+ break;
+ case 'f':
+ c.from_dir = (char *)optarg;
+ break;
+ case 't':
+ c.mount_point = (char *)optarg;
+ break;
+ default:
+ err = EUNKNOWN_OPT;
+ break;
+ }
+ if (err != NOERROR)
+ break;
+ }
}
- config.device_name = argv[optind];
+ if (argc > optind) {
+ c.dbg_lv = 0;
+ err = EUNKNOWN_ARG;
+ }
+ if (err == NOERROR)
+ return;
+
+ /* print out error */
+ switch (err) {
+ case EWRONG_OPT:
+ MSG(0, "\tError: Wrong option -%c %s\n", option, optarg);
+ break;
+ case ENEED_ARG:
+ MSG(0, "\tError: Need argument for -%c\n", option);
+ break;
+ case EUNKNOWN_OPT:
+ MSG(0, "\tError: Unknown option %c\n", option);
+ break;
+ case EUNKNOWN_ARG:
+ MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
+ break;
+ }
+ error_out();
}
static void do_fsck(struct f2fs_sb_info *sbi)
{
+ struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ u32 flag = le32_to_cpu(ckpt->ckpt_flags);
u32 blk_cnt;
fsck_init(sbi);
+ print_cp_state(flag);
+
+ if (!c.fix_on && !c.bug_on) {
+ switch (c.preen_mode) {
+ case PREEN_MODE_1:
+ if (fsck_chk_meta(sbi)) {
+ MSG(0, "[FSCK] F2FS metadata [Fail]");
+ MSG(0, "\tError: meta does not match, "
+ "force check all\n");
+ } else {
+ MSG(0, "[FSCK] F2FS metadata [Ok..]");
+ fsck_free(sbi);
+ return;
+ }
+
+ if (!c.ro)
+ c.fix_on = 1;
+ break;
+ }
+ } else {
+ /*
+ * we can hit this in 3 situations:
+ * 1. fsck -f, fix_on has already been set to 1 when
+ * parsing options;
+ * 2. fsck -a && CP_FSCK_FLAG is set, fix_on has already
+ * been set to 1 when checking CP_FSCK_FLAG;
+ * 3. fsck -p 1 && error is detected, then bug_on is set,
+ * we set fix_on = 1 here, so that fsck can fix errors
+ * automatically
+ */
+ c.fix_on = 1;
+ }
+
fsck_chk_orphan_node(sbi);
/* Traverse all block recursively from root inode */
blk_cnt = 1;
- fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
- F2FS_FT_DIR, TYPE_INODE, &blk_cnt);
+ fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, (u8 *)"/",
+ F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
fsck_verify(sbi);
fsck_free(sbi);
}
static void do_dump(struct f2fs_sb_info *sbi)
{
- struct dump_option *opt = (struct dump_option *)config.private;
+ struct dump_option *opt = (struct dump_option *)c.private;
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
u32 flag = le32_to_cpu(ckpt->ckpt_flags);
- fsck_init(sbi);
-
+ if (opt->end_nat == -1)
+ opt->end_nat = NM_I(sbi)->max_nid;
if (opt->end_sit == -1)
opt->end_sit = SM_I(sbi)->main_segments;
if (opt->end_ssa == -1)
opt->end_ssa = SM_I(sbi)->main_segments;
+ if (opt->start_nat != -1)
+ nat_dump(sbi);
if (opt->start_sit != -1)
sit_dump(sbi, opt->start_sit, opt->end_sit);
if (opt->start_ssa != -1)
ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
- if (opt->blk_addr != -1) {
+ if (opt->blk_addr != -1)
dump_info_from_blkaddr(sbi, opt->blk_addr);
- goto cleanup;
+ if (opt->nid)
+ dump_node(sbi, opt->nid, 0);
+
+ print_cp_state(flag);
+
+}
+
+static int do_defrag(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+ if (c.defrag_start > get_sb(block_count))
+ goto out_range;
+ if (c.defrag_start < SM_I(sbi)->main_blkaddr)
+ c.defrag_start = SM_I(sbi)->main_blkaddr;
+
+ if (c.defrag_len == 0)
+ c.defrag_len = sbi->blocks_per_seg;
+
+ if (c.defrag_start + c.defrag_len > get_sb(block_count))
+ c.defrag_len = get_sb(block_count) - c.defrag_start;
+
+ if (c.defrag_target == 0) {
+ c.defrag_target = c.defrag_start - 1;
+ if (!c.defrag_shrink)
+ c.defrag_target += c.defrag_len + 1;
}
- MSG(0, "Info: checkpoint state = %x : ", flag);
- if (flag & CP_FSCK_FLAG)
- MSG(0, "%s", " fsck");
- if (flag & CP_ERROR_FLAG)
- MSG(0, "%s", " error");
- if (flag & CP_COMPACT_SUM_FLAG)
- MSG(0, "%s", " compacted_summary");
- if (flag & CP_ORPHAN_PRESENT_FLAG)
- MSG(0, "%s", " orphan_inodes");
- if (flag & CP_FASTBOOT_FLAG)
- MSG(0, "%s", " fastboot");
- if (flag & CP_UMOUNT_FLAG)
- MSG(0, "%s", " unmount");
- else
- MSG(0, "%s", " sudden-power-off");
- MSG(0, "\n");
+ if (c.defrag_target < SM_I(sbi)->main_blkaddr ||
+ c.defrag_target > get_sb(block_count))
+ goto out_range;
+ if (c.defrag_target >= c.defrag_start &&
+ c.defrag_target < c.defrag_start + c.defrag_len)
+ goto out_range;
- dump_node(sbi, opt->nid);
-cleanup:
- fsck_free(sbi);
+ if (c.defrag_start > c.defrag_target)
+ MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n",
+ c.defrag_target,
+ c.defrag_start,
+ c.defrag_start + c.defrag_len - 1);
+ else
+ MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n",
+ c.defrag_start,
+ c.defrag_start + c.defrag_len - 1,
+ c.defrag_target);
+
+ return f2fs_defragment(sbi, c.defrag_start, c.defrag_len,
+ c.defrag_target, c.defrag_shrink);
+out_range:
+ ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"",
+ c.defrag_start,
+ c.defrag_start + c.defrag_len - 1,
+ c.defrag_target);
+ return -1;
+}
+
+static int do_resize(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+ if (!c.target_sectors)
+ c.target_sectors = c.total_sectors;
+
+ if (c.target_sectors > c.total_sectors) {
+ ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
+ c.target_sectors, c.total_sectors);
+ return -1;
+ }
+
+ if (c.target_sectors <=
+ (get_sb(block_count) << get_sb(log_sectors_per_block))) {
+ ASSERT_MSG("Nothing to resize, now only support resize to expand\n");
+ return -1;
+ }
+ return f2fs_resize(sbi);
+}
+
+static int do_sload(struct f2fs_sb_info *sbi)
+{
+ if (!c.from_dir) {
+ MSG(0, "\tError: Need source directory\n");
+ sload_usage();
+ return -1;
+ }
+ if (!c.mount_point)
+ c.mount_point = "/";
+
+ return f2fs_sload(sbi, c.from_dir, c.mount_point, NULL, NULL);
}
int main(int argc, char **argv)
@@ -202,15 +561,24 @@
struct f2fs_sb_info *sbi;
int ret = 0;
- f2fs_init_configuration(&config);
+ f2fs_init_configuration();
f2fs_parse_options(argc, argv);
- if (f2fs_dev_is_umounted(&config) < 0)
- return -1;
+ if (f2fs_devs_are_umounted() < 0) {
+ if (!c.ro || c.func == DEFRAG) {
+ MSG(0, "\tError: Not available on mounted device!\n");
+ return -1;
+ }
+
+ /* allow ro-mounted partition */
+ MSG(0, "Info: Check FS only due to RO\n");
+ c.fix_on = 0;
+ c.auto_fix = 0;
+ }
/* Get device */
- if (f2fs_get_device_info(&config) < 0)
+ if (f2fs_get_device_info() < 0)
return -1;
fsck_again:
memset(&gfsck, 0, sizeof(gfsck));
@@ -218,44 +586,66 @@
sbi = &gfsck.sbi;
ret = f2fs_do_mount(sbi);
- if (ret == 1) {
- free(sbi->ckpt);
- free(sbi->raw_super);
- goto out;
- } else if (ret < 0)
- return -1;
+ if (ret != 0) {
+ if (ret == 1) {
+ MSG(0, "Info: No error was reported\n");
+ ret = 0;
+ }
+ goto out_err;
+ }
- switch (config.func) {
+ switch (c.func) {
case FSCK:
do_fsck(sbi);
break;
case DUMP:
do_dump(sbi);
break;
+#ifndef WITH_ANDROID
+ case DEFRAG:
+ ret = do_defrag(sbi);
+ if (ret)
+ goto out_err;
+ break;
+ case RESIZE:
+ if (do_resize(sbi))
+ goto out_err;
+ break;
+ case SLOAD:
+ do_sload(sbi);
+ break;
+#endif
}
f2fs_do_umount(sbi);
-out:
- if (config.func == FSCK && config.bug_on) {
- if (config.fix_on == 0 && config.auto_fix == 0) {
+
+ if (c.func == FSCK && c.bug_on) {
+ if (!c.ro && c.fix_on == 0 && c.auto_fix == 0) {
char ans[255] = {0};
retry:
printf("Do you want to fix this partition? [Y/N] ");
ret = scanf("%s", ans);
ASSERT(ret >= 0);
if (!strcasecmp(ans, "y"))
- config.fix_on = 1;
+ c.fix_on = 1;
else if (!strcasecmp(ans, "n"))
- config.fix_on = 0;
+ c.fix_on = 0;
else
goto retry;
- if (config.fix_on)
+ if (c.fix_on)
goto fsck_again;
}
}
- f2fs_finalize_device(&config);
+ f2fs_finalize_device();
printf("\nDone.\n");
return 0;
+
+out_err:
+ if (sbi->ckpt)
+ free(sbi->ckpt);
+ if (sbi->raw_super)
+ free(sbi->raw_super);
+ return ret;
}
diff --git a/fsck/mount.c b/fsck/mount.c
index 6d96db8..b8e8c45 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -11,14 +11,43 @@
#include "fsck.h"
#include <locale.h>
+u32 get_free_segments(struct f2fs_sb_info *sbi)
+{
+ u32 i, free_segs = 0;
+
+ for (i = 0; i < TOTAL_SEGS(sbi); i++) {
+ struct seg_entry *se = get_seg_entry(sbi, i);
+
+ if (se->valid_blocks == 0x0 &&
+ !IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE))
+ free_segs++;
+ }
+ 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 char en[F2FS_NAME_LEN + 1];
unsigned int i = 0;
int namelen = le32_to_cpu(inode->i_namelen);
+ int is_encrypt = file_is_encrypt(inode);
+ namelen = convert_encrypted_name(inode->i_name, namelen, en, is_encrypt);
+ en[namelen] = '\0';
if (name && namelen) {
inode->i_name[namelen] = '\0';
- MSG(0, " - File name : %s\n", inode->i_name);
+ MSG(0, " - File name : %s%s\n", en,
+ is_encrypt ? " <encrypted>" : "");
setlocale(LC_ALL, "");
MSG(0, " - File size : %'llu (bytes)\n",
le64_to_cpu(inode->i_size));
@@ -26,6 +55,7 @@
}
DISP_u32(inode, i_mode);
+ DISP_u32(inode, i_advise);
DISP_u32(inode, i_uid);
DISP_u32(inode, i_gid);
DISP_u32(inode, i_links);
@@ -45,17 +75,17 @@
DISP_u32(inode, i_flags);
DISP_u32(inode, i_inline);
DISP_u32(inode, i_pino);
+ DISP_u32(inode, i_dir_level);
if (namelen) {
DISP_u32(inode, i_namelen);
- inode->i_name[namelen] = '\0';
- DISP_utf(inode, i_name);
+ printf("%-30s\t\t[%s]\n", "i_name", en);
}
printf("i_ext: fofs:%x blkaddr:%x len:%x\n",
- inode->i_ext.fofs,
- inode->i_ext.blk_addr,
- inode->i_ext.len);
+ le32_to_cpu(inode->i_ext.fofs),
+ le32_to_cpu(inode->i_ext.blk_addr),
+ le32_to_cpu(inode->i_ext.len));
DISP_u32(inode, i_addr[0]); /* Pointers to data blocks */
DISP_u32(inode, i_addr[1]); /* Pointers to data blocks */
@@ -65,7 +95,7 @@
for (i = 4; i < ADDRS_PER_INODE(inode); i++) {
if (inode->i_addr[i] != 0x0) {
printf("i_addr[0x%x] points data block\r\t\t[0x%4x]\n",
- i, inode->i_addr[i]);
+ i, le32_to_cpu(inode->i_addr[i]));
break;
}
}
@@ -79,30 +109,37 @@
printf("\n");
}
-void print_node_info(struct f2fs_node *node_block)
+void print_node_info(struct f2fs_node *node_block, int verbose)
{
nid_t ino = le32_to_cpu(node_block->footer.ino);
nid_t nid = le32_to_cpu(node_block->footer.nid);
/* Is this inode? */
if (ino == nid) {
- DBG(0, "Node ID [0x%x:%u] is inode\n", nid, nid);
- print_inode_info(&node_block->i, 0);
+ DBG(verbose, "Node ID [0x%x:%u] is inode\n", nid, nid);
+ print_inode_info(&node_block->i, verbose);
} else {
int i;
u32 *dump_blk = (u32 *)node_block;
- DBG(0, "Node ID [0x%x:%u] is direct node or indirect node.\n",
+ DBG(verbose,
+ "Node ID [0x%x:%u] is direct node or indirect node.\n",
nid, nid);
for (i = 0; i <= 10; i++)
- MSG(0, "[%d]\t\t\t[0x%8x : %d]\n",
+ MSG(verbose, "[%d]\t\t\t[0x%8x : %d]\n",
i, dump_blk[i], dump_blk[i]);
}
}
-void print_raw_sb_info(struct f2fs_sb_info *sbi)
+static void DISP_label(u_int16_t *name)
{
- struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ char buffer[MAX_VOLUME_NAME];
- if (!config.dbg_lv)
+ utf16_to_utf8(buffer, name, MAX_VOLUME_NAME, MAX_VOLUME_NAME);
+ printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
+}
+
+void print_raw_sb_info(struct f2fs_super_block *sb)
+{
+ if (!c.dbg_lv)
return;
printf("\n");
@@ -112,6 +149,9 @@
DISP_u32(sb, magic);
DISP_u32(sb, major_ver);
+
+ DISP_label(sb->volume_name);
+
DISP_u32(sb, minor_ver);
DISP_u32(sb, log_sectorsize);
DISP_u32(sb, log_sectors_per_block);
@@ -151,7 +191,7 @@
{
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
- if (!config.dbg_lv)
+ if (!c.dbg_lv)
return;
printf("\n");
@@ -204,35 +244,173 @@
printf("\n\n");
}
-int sanity_check_raw_super(struct f2fs_super_block *raw_super)
+void print_cp_state(u32 flag)
+{
+ MSG(0, "Info: checkpoint state = %x : ", flag);
+ if (flag & CP_FSCK_FLAG)
+ MSG(0, "%s", " fsck");
+ if (flag & CP_ERROR_FLAG)
+ MSG(0, "%s", " error");
+ if (flag & CP_COMPACT_SUM_FLAG)
+ MSG(0, "%s", " compacted_summary");
+ if (flag & CP_ORPHAN_PRESENT_FLAG)
+ MSG(0, "%s", " orphan_inodes");
+ if (flag & CP_FASTBOOT_FLAG)
+ MSG(0, "%s", " fastboot");
+ if (flag & CP_UMOUNT_FLAG)
+ MSG(0, "%s", " unmount");
+ else
+ MSG(0, "%s", " sudden-power-off");
+ MSG(0, "\n");
+}
+
+void print_sb_state(struct f2fs_super_block *sb)
+{
+ __le32 f = sb->feature;
+ int i;
+
+ MSG(0, "Info: superblock features = %x : ", f);
+ if (f & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) {
+ MSG(0, "%s", " encrypt");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_BLKZONED)) {
+ MSG(0, "%s", " zoned block device");
+ }
+ MSG(0, "\n");
+ MSG(0, "Info: superblock encrypt level = %d, salt = ",
+ sb->encryption_level);
+ for (i = 0; i < 16; i++)
+ MSG(0, "%02x", sb->encrypt_pw_salt[i]);
+ MSG(0, "\n");
+}
+
+static inline int sanity_check_area_boundary(struct f2fs_super_block *sb,
+ u64 offset)
+{
+ u32 segment0_blkaddr = get_sb(segment0_blkaddr);
+ u32 cp_blkaddr = get_sb(cp_blkaddr);
+ u32 sit_blkaddr = get_sb(sit_blkaddr);
+ u32 nat_blkaddr = get_sb(nat_blkaddr);
+ u32 ssa_blkaddr = get_sb(ssa_blkaddr);
+ u32 main_blkaddr = get_sb(main_blkaddr);
+ u32 segment_count_ckpt = get_sb(segment_count_ckpt);
+ u32 segment_count_sit = get_sb(segment_count_sit);
+ u32 segment_count_nat = get_sb(segment_count_nat);
+ u32 segment_count_ssa = get_sb(segment_count_ssa);
+ u32 segment_count_main = get_sb(segment_count_main);
+ u32 segment_count = get_sb(segment_count);
+ u32 log_blocks_per_seg = get_sb(log_blocks_per_seg);
+ u64 main_end_blkaddr = main_blkaddr +
+ (segment_count_main << log_blocks_per_seg);
+ u64 seg_end_blkaddr = segment0_blkaddr +
+ (segment_count << log_blocks_per_seg);
+
+ if (segment0_blkaddr != cp_blkaddr) {
+ MSG(0, "\tMismatch segment0(%u) cp_blkaddr(%u)\n",
+ segment0_blkaddr, cp_blkaddr);
+ return -1;
+ }
+
+ if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) !=
+ sit_blkaddr) {
+ MSG(0, "\tWrong CP boundary, start(%u) end(%u) blocks(%u)\n",
+ cp_blkaddr, sit_blkaddr,
+ segment_count_ckpt << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) !=
+ nat_blkaddr) {
+ MSG(0, "\tWrong SIT boundary, start(%u) end(%u) blocks(%u)\n",
+ sit_blkaddr, nat_blkaddr,
+ segment_count_sit << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) !=
+ ssa_blkaddr) {
+ MSG(0, "\tWrong NAT boundary, start(%u) end(%u) blocks(%u)\n",
+ nat_blkaddr, ssa_blkaddr,
+ segment_count_nat << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) !=
+ main_blkaddr) {
+ MSG(0, "\tWrong SSA boundary, start(%u) end(%u) blocks(%u)\n",
+ ssa_blkaddr, main_blkaddr,
+ segment_count_ssa << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (main_end_blkaddr > seg_end_blkaddr) {
+ MSG(0, "\tWrong MAIN_AREA, start(%u) end(%u) block(%u)\n",
+ main_blkaddr,
+ segment0_blkaddr +
+ (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ return -1;
+ } else if (main_end_blkaddr < seg_end_blkaddr) {
+ int err;
+
+ set_sb(segment_count, (main_end_blkaddr -
+ segment0_blkaddr) >> log_blocks_per_seg);
+
+ err = dev_write(sb, offset, sizeof(struct f2fs_super_block));
+ MSG(0, "Info: Fix alignment: %s, start(%u) end(%u) block(%u)\n",
+ err ? "failed": "done",
+ main_blkaddr,
+ segment0_blkaddr +
+ (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ }
+ return 0;
+}
+
+int sanity_check_raw_super(struct f2fs_super_block *sb, u64 offset)
{
unsigned int blocksize;
- if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
+ if (F2FS_SUPER_MAGIC != get_sb(magic))
+ return -1;
+
+ if (F2FS_BLKSIZE != PAGE_CACHE_SIZE)
+ return -1;
+
+ blocksize = 1 << get_sb(log_blocksize);
+ if (F2FS_BLKSIZE != blocksize)
+ return -1;
+
+ /* check log blocks per segment */
+ if (get_sb(log_blocks_per_seg) != 9)
+ return -1;
+
+ /* Currently, support 512/1024/2048/4096 bytes sector size */
+ if (get_sb(log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
+ get_sb(log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE)
+ return -1;
+
+ if (get_sb(log_sectors_per_block) + get_sb(log_sectorsize) !=
+ F2FS_MAX_LOG_SECTOR_SIZE)
+ return -1;
+
+ /* check reserved ino info */
+ if (get_sb(node_ino) != 1 || get_sb(meta_ino) != 2 ||
+ get_sb(root_ino) != 3)
+ return -1;
+
+ /* Check zoned block device feature */
+ if (c.devices[0].zoned_model == F2FS_ZONED_HM &&
+ !(sb->feature & cpu_to_le32(F2FS_FEATURE_BLKZONED))) {
+ MSG(0, "\tMissing zoned block device feature\n");
return -1;
}
- if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) {
+ if (get_sb(segment_count) > F2FS_MAX_SEGMENT)
return -1;
- }
- blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
- if (F2FS_BLKSIZE != blocksize) {
+ if (sanity_check_area_boundary(sb, offset))
return -1;
- }
-
- if (le32_to_cpu(raw_super->log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
- le32_to_cpu(raw_super->log_sectorsize) <
- F2FS_MIN_LOG_SECTOR_SIZE) {
- return -1;
- }
-
- if (le32_to_cpu(raw_super->log_sectors_per_block) +
- le32_to_cpu(raw_super->log_sectorsize) !=
- F2FS_MAX_LOG_SECTOR_SIZE) {
- return -1;
- }
-
return 0;
}
@@ -250,40 +428,42 @@
if (dev_read(sbi->raw_super, offset, sizeof(struct f2fs_super_block)))
return -1;
- if (!sanity_check_raw_super(sbi->raw_super)) {
+ if (!sanity_check_raw_super(sbi->raw_super, offset)) {
/* get kernel version */
- if (config.kd >= 0) {
- dev_read_version(config.version, 0, VERSION_LEN);
- get_kernel_version(config.version);
+ if (c.kd >= 0) {
+ dev_read_version(c.version, 0, VERSION_LEN);
+ get_kernel_version(c.version);
} else {
- memset(config.version, 0, VERSION_LEN);
+ memset(c.version, 0, VERSION_LEN);
}
/* build sb version */
- memcpy(config.sb_version, sbi->raw_super->version, VERSION_LEN);
- get_kernel_version(config.sb_version);
- memcpy(config.init_version, sbi->raw_super->init_version, VERSION_LEN);
- get_kernel_version(config.init_version);
+ memcpy(c.sb_version, sbi->raw_super->version, VERSION_LEN);
+ get_kernel_version(c.sb_version);
+ memcpy(c.init_version, sbi->raw_super->init_version, VERSION_LEN);
+ get_kernel_version(c.init_version);
- MSG(0, "Info: MKFS version\n \"%s\"\n", config.init_version);
+ MSG(0, "Info: MKFS version\n \"%s\"\n", c.init_version);
MSG(0, "Info: FSCK version\n from \"%s\"\n to \"%s\"\n",
- config.sb_version, config.version);
- if (memcmp(config.sb_version, config.version, VERSION_LEN)) {
+ c.sb_version, c.version);
+ if (memcmp(c.sb_version, c.version, VERSION_LEN)) {
int ret;
memcpy(sbi->raw_super->version,
- config.version, VERSION_LEN);
+ c.version, VERSION_LEN);
ret = dev_write(sbi->raw_super, offset,
sizeof(struct f2fs_super_block));
ASSERT(ret >= 0);
- config.auto_fix = 0;
- config.fix_on = 1;
+ c.auto_fix = 0;
+ c.fix_on = 1;
}
+ print_sb_state(sbi->raw_super);
return 0;
}
free(sbi->raw_super);
+ sbi->raw_super = NULL;
MSG(0, "\tCan't find a valid F2FS superblock at 0x%x\n", block);
return -EINVAL;
@@ -291,24 +471,60 @@
int init_sb_info(struct f2fs_sb_info *sbi)
{
- struct f2fs_super_block *raw_super = sbi->raw_super;
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ u64 total_sectors;
+ int i;
- sbi->log_sectors_per_block =
- le32_to_cpu(raw_super->log_sectors_per_block);
- sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize);
+ sbi->log_sectors_per_block = get_sb(log_sectors_per_block);
+ sbi->log_blocksize = get_sb(log_blocksize);
sbi->blocksize = 1 << sbi->log_blocksize;
- sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
+ sbi->log_blocks_per_seg = get_sb(log_blocks_per_seg);
sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg;
- sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
- sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone);
- sbi->total_sections = le32_to_cpu(raw_super->section_count);
- sbi->total_node_count =
- (le32_to_cpu(raw_super->segment_count_nat) / 2)
- * sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK;
- sbi->root_ino_num = le32_to_cpu(raw_super->root_ino);
- sbi->node_ino_num = le32_to_cpu(raw_super->node_ino);
- sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino);
+ sbi->segs_per_sec = get_sb(segs_per_sec);
+ sbi->secs_per_zone = get_sb(secs_per_zone);
+ sbi->total_sections = get_sb(section_count);
+ sbi->total_node_count = (get_sb(segment_count_nat) / 2) *
+ sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK;
+ sbi->root_ino_num = get_sb(root_ino);
+ sbi->node_ino_num = get_sb(node_ino);
+ sbi->meta_ino_num = get_sb(meta_ino);
sbi->cur_victim_sec = NULL_SEGNO;
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ if (!sb->devs[i].path[0])
+ break;
+
+ if (i) {
+ c.devices[i].path = strdup((char *)sb->devs[i].path);
+ if (get_device_info(i))
+ ASSERT(0);
+ } else {
+ ASSERT(!strcmp((char *)sb->devs[i].path,
+ (char *)c.devices[i].path));
+ }
+
+ c.devices[i].total_segments =
+ le32_to_cpu(sb->devs[i].total_segments);
+ if (i)
+ c.devices[i].start_blkaddr =
+ c.devices[i - 1].end_blkaddr + 1;
+ c.devices[i].end_blkaddr = c.devices[i].start_blkaddr +
+ c.devices[i].total_segments *
+ c.blks_per_seg - 1;
+ if (i == 0)
+ c.devices[i].end_blkaddr += get_sb(segment0_blkaddr);
+
+ c.ndevs = i + 1;
+ MSG(0, "Info: Device[%d] : %s blkaddr = %"PRIx64"--%"PRIx64"\n",
+ i, c.devices[i].path,
+ c.devices[i].start_blkaddr,
+ c.devices[i].end_blkaddr);
+ }
+
+ total_sectors = get_sb(block_count) << sbi->log_sectors_per_block;
+ MSG(0, "Info: total FS sectors = %"PRIu64" (%"PRIu64" MB)\n",
+ total_sectors, total_sectors >>
+ (20 - get_sb(log_sectorsize)));
return 0;
}
@@ -316,7 +532,7 @@
unsigned long long *version)
{
void *cp_page_1, *cp_page_2;
- struct f2fs_checkpoint *cp_block;
+ struct f2fs_checkpoint *cp;
unsigned long blk_size = sbi->blocksize;
unsigned long long cur_version = 0, pre_version = 0;
unsigned int crc = 0;
@@ -325,36 +541,36 @@
/* Read the 1st cp block in this CP pack */
cp_page_1 = malloc(PAGE_SIZE);
if (dev_read_block(cp_page_1, cp_addr) < 0)
- return NULL;
+ goto invalid_cp1;
- cp_block = (struct f2fs_checkpoint *)cp_page_1;
- crc_offset = le32_to_cpu(cp_block->checksum_offset);
+ cp = (struct f2fs_checkpoint *)cp_page_1;
+ crc_offset = get_cp(checksum_offset);
if (crc_offset >= blk_size)
goto invalid_cp1;
- crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
- if (f2fs_crc_valid(crc, cp_block, crc_offset))
+ crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
+ if (f2fs_crc_valid(crc, cp, crc_offset))
goto invalid_cp1;
- pre_version = le64_to_cpu(cp_block->checkpoint_ver);
+ pre_version = get_cp(checkpoint_ver);
/* Read the 2nd cp block in this CP pack */
cp_page_2 = malloc(PAGE_SIZE);
- cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
+ cp_addr += get_cp(cp_pack_total_block_count) - 1;
if (dev_read_block(cp_page_2, cp_addr) < 0)
goto invalid_cp2;
- cp_block = (struct f2fs_checkpoint *)cp_page_2;
- crc_offset = le32_to_cpu(cp_block->checksum_offset);
+ cp = (struct f2fs_checkpoint *)cp_page_2;
+ crc_offset = get_cp(checksum_offset);
if (crc_offset >= blk_size)
goto invalid_cp2;
- crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
- if (f2fs_crc_valid(crc, cp_block, crc_offset))
+ crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
+ if (f2fs_crc_valid(crc, cp, crc_offset))
goto invalid_cp2;
- cur_version = le64_to_cpu(cp_block->checkpoint_ver);
+ cur_version = get_cp(checkpoint_ver);
if (cur_version == pre_version) {
*version = cur_version;
@@ -371,14 +587,19 @@
int get_valid_checkpoint(struct f2fs_sb_info *sbi)
{
- struct f2fs_super_block *raw_sb = sbi->raw_super;
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
void *cp1, *cp2, *cur_page;
unsigned long blk_size = sbi->blocksize;
- unsigned long long cp1_version = 0, cp2_version = 0;
+ unsigned long long cp1_version = 0, cp2_version = 0, version;
unsigned long long cp_start_blk_no;
- unsigned int cp_blks = 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
+ unsigned int cp_payload, cp_blks;
int ret;
+ cp_payload = get_sb(cp_payload);
+ if (cp_payload > F2FS_BLK_ALIGN(MAX_SIT_BITMAP_SIZE))
+ return -EINVAL;
+
+ cp_blks = 1 + cp_payload;
sbi->ckpt = malloc(cp_blks * blk_size);
if (!sbi->ckpt)
return -ENOMEM;
@@ -386,32 +607,35 @@
* Finding out valid cp block involves read both
* sets( cp pack1 and cp pack 2)
*/
- cp_start_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+ cp_start_blk_no = get_sb(cp_blkaddr);
cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version);
/* The second checkpoint pack should start at the next segment */
- cp_start_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg);
+ cp_start_blk_no += 1 << get_sb(log_blocks_per_seg);
cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version);
if (cp1 && cp2) {
if (ver_after(cp2_version, cp1_version)) {
cur_page = cp2;
sbi->cur_cp = 2;
+ version = cp2_version;
} else {
cur_page = cp1;
sbi->cur_cp = 1;
+ version = cp1_version;
}
} else if (cp1) {
cur_page = cp1;
sbi->cur_cp = 1;
+ version = cp1_version;
} else if (cp2) {
cur_page = cp2;
sbi->cur_cp = 2;
- } else {
- free(cp1);
- free(cp2);
+ version = cp2_version;
+ } else
goto fail_no_cp;
- }
+
+ MSG(0, "Info: CKPT version = %llx\n", version);
memcpy(sbi->ckpt, cur_page, blk_size);
@@ -419,10 +643,10 @@
unsigned int i;
unsigned long long cp_blk_no;
- cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr);
+ cp_blk_no = get_sb(cp_blkaddr);
if (cur_page == cp2)
- cp_blk_no += 1 <<
- le32_to_cpu(raw_sb->log_blocks_per_seg);
+ cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
/* copy sit bitmap */
for (i = 1; i < cp_blks; i++) {
unsigned char *ckpt = (unsigned char *)sbi->ckpt;
@@ -431,27 +655,30 @@
memcpy(ckpt + i * blk_size, cur_page, blk_size);
}
}
- free(cp1);
- free(cp2);
+ if (cp1)
+ free(cp1);
+ if (cp2)
+ free(cp2);
return 0;
fail_no_cp:
free(sbi->ckpt);
+ sbi->ckpt = NULL;
return -EINVAL;
}
int sanity_check_ckpt(struct f2fs_sb_info *sbi)
{
unsigned int total, fsmeta;
- struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
- total = le32_to_cpu(raw_super->segment_count);
- fsmeta = le32_to_cpu(raw_super->segment_count_ckpt);
- fsmeta += le32_to_cpu(raw_super->segment_count_sit);
- fsmeta += le32_to_cpu(raw_super->segment_count_nat);
- fsmeta += le32_to_cpu(ckpt->rsvd_segment_count);
- fsmeta += le32_to_cpu(raw_super->segment_count_ssa);
+ total = get_sb(segment_count);
+ fsmeta = get_sb(segment_count_ckpt);
+ fsmeta += get_sb(segment_count_sit);
+ fsmeta += get_sb(segment_count_nat);
+ fsmeta += get_cp(rsvd_segment_count);
+ fsmeta += get_sb(segment_count_ssa);
if (fsmeta >= total)
return 1;
@@ -459,23 +686,92 @@
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 (!(c.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_raw = F2FS_RAW_SUPER(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
struct f2fs_nm_info *nm_i = NM_I(sbi);
unsigned char *version_bitmap;
unsigned int nat_segs, nat_blocks;
- nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr);
+ nm_i->nat_blkaddr = get_sb(nat_blkaddr);
/* segment_count_nat includes pair segment so divide to 2. */
- nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1;
- nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg);
+ nat_segs = get_sb(segment_count_nat) >> 1;
+ nat_blocks = nat_segs << get_sb(log_blocks_per_seg);
nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
nm_i->fcnt = 0;
nm_i->nat_cnt = 0;
- nm_i->init_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
- nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
+ nm_i->init_scan_nid = get_cp(next_free_nid);
+ nm_i->next_scan_nid = get_cp(next_free_nid);
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
@@ -488,7 +784,7 @@
/* 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)
@@ -507,8 +803,8 @@
int build_sit_info(struct f2fs_sb_info *sbi)
{
- struct f2fs_super_block *raw_sb = F2FS_RAW_SUPER(sbi);
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
struct sit_info *sit_i;
unsigned int sit_segs, start;
char *src_bitmap, *dst_bitmap;
@@ -521,6 +817,8 @@
SM_I(sbi)->sit_info = sit_i;
sit_i->sentries = calloc(TOTAL_SEGS(sbi) * sizeof(struct seg_entry), 1);
+ if (!sit_i->sentries)
+ return -ENOMEM;
for (start = 0; start < TOTAL_SEGS(sbi); start++) {
sit_i->sentries[start].cur_valid_map
@@ -532,21 +830,21 @@
return -ENOMEM;
}
- sit_segs = le32_to_cpu(raw_sb->segment_count_sit) >> 1;
+ sit_segs = get_sb(segment_count_sit) >> 1;
bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP);
dst_bitmap = malloc(bitmap_size);
memcpy(dst_bitmap, src_bitmap, bitmap_size);
- sit_i->sit_base_addr = le32_to_cpu(raw_sb->sit_blkaddr);
+ sit_i->sit_base_addr = get_sb(sit_blkaddr);
sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg;
- sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count);
+ sit_i->written_valid_blocks = get_cp(valid_block_count);
sit_i->sit_bitmap = dst_bitmap;
sit_i->bitmap_size = bitmap_size;
sit_i->dirty_sentries = 0;
sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK;
- sit_i->elapsed_time = le64_to_cpu(ckpt->elapsed_time);
+ sit_i->elapsed_time = get_cp(elapsed_time);
return 0;
}
@@ -581,10 +879,10 @@
ASSERT(ret >= 0);
curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
- memcpy(&curseg->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE);
+ memcpy(&curseg->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE);
curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
- memcpy(&curseg->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE,
+ memcpy(&curseg->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE,
SUM_JOURNAL_SIZE);
offset = 2 * SUM_JOURNAL_SIZE;
@@ -599,6 +897,8 @@
else
blk_off = curseg->next_blkoff;
+ ASSERT(blk_off <= ENTRIES_IN_SUM);
+
for (j = 0; j < blk_off; j++) {
struct f2fs_summary *s;
s = (struct f2fs_summary *)(kaddr + offset);
@@ -643,7 +943,7 @@
static void read_normal_summaries(struct f2fs_sb_info *sbi, int type)
{
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
struct f2fs_summary_block *sum_blk;
struct curseg_info *curseg;
unsigned int segno = 0;
@@ -651,15 +951,14 @@
int ret;
if (IS_DATASEG(type)) {
- segno = le32_to_cpu(ckpt->cur_data_segno[type]);
- if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+ segno = get_cp(cur_data_segno[type]);
+ if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type);
else
blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type);
} else {
- segno = le32_to_cpu(ckpt->cur_node_segno[type -
- CURSEG_HOT_NODE]);
- if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+ segno = get_cp(cur_node_segno[type - CURSEG_HOT_NODE]);
+ if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE,
type - CURSEG_HOT_NODE);
else
@@ -670,7 +969,7 @@
ret = dev_read_block(sum_blk, blk_addr);
ASSERT(ret >= 0);
- if (IS_NODESEG(type) && !is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG))
+ if (IS_NODESEG(type) && !is_set_ckpt_flags(cp, CP_UMOUNT_FLAG))
restore_node_summary(sbi, segno, sum_blk);
curseg = CURSEG_I(sbi, type);
@@ -679,6 +978,36 @@
free(sum_blk);
}
+void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr,
+ struct f2fs_summary *sum)
+{
+ struct f2fs_summary_block *sum_blk;
+ u32 segno, offset;
+ int type, ret;
+ struct seg_entry *se;
+
+ segno = GET_SEGNO(sbi, blk_addr);
+ offset = OFFSET_IN_SEG(sbi, blk_addr);
+
+ se = get_seg_entry(sbi, segno);
+
+ sum_blk = get_sum_block(sbi, segno, &type);
+ memcpy(&sum_blk->entries[offset], sum, sizeof(*sum));
+ sum_blk->footer.entry_type = IS_NODESEG(se->type) ? SUM_TYPE_NODE :
+ SUM_TYPE_DATA;
+
+ /* write SSA all the time */
+ if (type < SEG_TYPE_MAX) {
+ u64 ssa_blk = GET_SUM_BLKADDR(sbi, segno);
+ ret = dev_write_block(sum_blk, ssa_blk);
+ ASSERT(ret >= 0);
+ }
+
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+}
+
static void restore_curseg_summaries(struct f2fs_sb_info *sbi)
{
int type = CURSEG_HOT_DATA;
@@ -694,7 +1023,7 @@
static void build_curseg(struct f2fs_sb_info *sbi)
{
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
struct curseg_info *array;
unsigned short blk_off;
unsigned int segno;
@@ -709,39 +1038,42 @@
array[i].sum_blk = malloc(PAGE_CACHE_SIZE);
ASSERT(array[i].sum_blk);
if (i <= CURSEG_COLD_DATA) {
- blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]);
- segno = le32_to_cpu(ckpt->cur_data_segno[i]);
+ blk_off = get_cp(cur_data_blkoff[i]);
+ segno = get_cp(cur_data_segno[i]);
}
if (i > CURSEG_COLD_DATA) {
- blk_off = le16_to_cpu(ckpt->cur_node_blkoff[i -
- CURSEG_HOT_NODE]);
- segno = le32_to_cpu(ckpt->cur_node_segno[i -
- CURSEG_HOT_NODE]);
+ blk_off = get_cp(cur_node_blkoff[i - CURSEG_HOT_NODE]);
+ segno = get_cp(cur_node_segno[i - CURSEG_HOT_NODE]);
}
+ ASSERT(segno < TOTAL_SEGS(sbi));
+ ASSERT(blk_off < DEFAULT_BLOCKS_PER_SEGMENT);
+
array[i].segno = segno;
array[i].zone = GET_ZONENO_FROM_SEGNO(sbi, segno);
array[i].next_segno = NULL_SEGNO;
array[i].next_blkoff = blk_off;
- array[i].alloc_type = ckpt->alloc_type[i];
+ array[i].alloc_type = cp->alloc_type[i];
}
restore_curseg_summaries(sbi);
}
-inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
+static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
{
unsigned int end_segno = SM_I(sbi)->segment_count - 1;
ASSERT(segno <= end_segno);
}
-static struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi,
+struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi,
unsigned int segno)
{
struct sit_info *sit_i = SIT_I(sbi);
unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno);
block_t blk_addr = sit_i->sit_base_addr + offset;
- struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
+ struct f2fs_sit_block *sit_blk;
int ret;
+ sit_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(sit_blk);
check_seg_range(sbi, segno);
/* calculate sit block address */
@@ -819,53 +1151,60 @@
return &sit_i->sentries[segno];
}
-int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno,
- struct f2fs_summary_block *sum_blk)
+struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi,
+ unsigned int segno, int *ret_type)
{
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ struct f2fs_summary_block *sum_blk;
struct curseg_info *curseg;
int type, ret;
u64 ssa_blk;
+ *ret_type= SEG_TYPE_MAX;
+
ssa_blk = GET_SUM_BLKADDR(sbi, segno);
for (type = 0; type < NR_CURSEG_NODE_TYPE; type++) {
- if (segno == ckpt->cur_node_segno[type]) {
+ if (segno == get_cp(cur_node_segno[type])) {
curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + type);
if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) {
ASSERT_MSG("segno [0x%x] indicates a data "
"segment, but should be node",
segno);
- return -EINVAL;
+ *ret_type = -SEG_TYPE_CUR_NODE;
+ } else {
+ *ret_type = SEG_TYPE_CUR_NODE;
}
- memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ);
- return SEG_TYPE_CUR_NODE;
+ return curseg->sum_blk;
}
}
for (type = 0; type < NR_CURSEG_DATA_TYPE; type++) {
- if (segno == ckpt->cur_data_segno[type]) {
+ if (segno == get_cp(cur_data_segno[type])) {
curseg = CURSEG_I(sbi, type);
if (IS_SUM_NODE_SEG(curseg->sum_blk->footer)) {
ASSERT_MSG("segno [0x%x] indicates a node "
"segment, but should be data",
segno);
- return -EINVAL;
+ *ret_type = -SEG_TYPE_CUR_DATA;
+ } else {
+ *ret_type = SEG_TYPE_CUR_DATA;
}
- DBG(2, "segno [0x%x] is current data seg[0x%x]\n",
- segno, type);
- memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ);
- return SEG_TYPE_CUR_DATA;
+ return curseg->sum_blk;
}
}
+ sum_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(sum_blk);
+
ret = dev_read_block(sum_blk, ssa_blk);
ASSERT(ret >= 0);
if (IS_SUM_NODE_SEG(sum_blk->footer))
- return SEG_TYPE_NODE;
- else
- return SEG_TYPE_DATA;
+ *ret_type = SEG_TYPE_NODE;
+ else if (IS_SUM_DATA_SEG(sum_blk->footer))
+ *ret_type = SEG_TYPE_DATA;
+ return sum_blk;
}
int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr,
@@ -873,45 +1212,36 @@
{
struct f2fs_summary_block *sum_blk;
u32 segno, offset;
- int ret;
+ int type;
segno = GET_SEGNO(sbi, blk_addr);
offset = OFFSET_IN_SEG(sbi, blk_addr);
- sum_blk = calloc(BLOCK_SZ, 1);
-
- ret = get_sum_block(sbi, segno, sum_blk);
+ sum_blk = get_sum_block(sbi, segno, &type);
memcpy(sum_entry, &(sum_blk->entries[offset]),
sizeof(struct f2fs_summary));
- free(sum_blk);
- return ret;
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX)
+ free(sum_blk);
+ return type;
}
static void get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
struct f2fs_nat_entry *raw_nat)
{
- struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_nat_block *nat_block;
- pgoff_t block_off;
pgoff_t block_addr;
- int seg_off, entry_off;
+ int entry_off;
int ret;
if (lookup_nat_in_journal(sbi, nid, raw_nat) >= 0)
return;
nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+ ASSERT(nat_block);
- block_off = nid / NAT_ENTRY_PER_BLOCK;
entry_off = nid % NAT_ENTRY_PER_BLOCK;
-
- 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;
+ block_addr = current_nat_addr(sbi, nid);
ret = dev_read_block(nat_block, block_addr);
ASSERT(ret >= 0);
@@ -921,6 +1251,82 @@
free(nat_block);
}
+void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid,
+ u16 ofs_in_node, block_t newaddr)
+{
+ struct f2fs_node *node_blk = NULL;
+ struct node_info ni;
+ block_t oldaddr, startaddr, endaddr;
+ int ret;
+
+ node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ get_node_info(sbi, nid, &ni);
+
+ /* read node_block */
+ ret = dev_read_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ /* check its block address */
+ if (node_blk->footer.nid == node_blk->footer.ino) {
+ oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs_in_node]);
+ node_blk->i.i_addr[ofs_in_node] = cpu_to_le32(newaddr);
+ } else {
+ oldaddr = le32_to_cpu(node_blk->dn.addr[ofs_in_node]);
+ node_blk->dn.addr[ofs_in_node] = cpu_to_le32(newaddr);
+ }
+
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ /* check extent cache entry */
+ if (node_blk->footer.nid != node_blk->footer.ino) {
+ get_node_info(sbi, le32_to_cpu(node_blk->footer.ino), &ni);
+
+ /* read inode block */
+ ret = dev_read_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ }
+
+ startaddr = le32_to_cpu(node_blk->i.i_ext.blk_addr);
+ endaddr = startaddr + le32_to_cpu(node_blk->i.i_ext.len);
+ if (oldaddr >= startaddr && oldaddr < endaddr) {
+ node_blk->i.i_ext.len = 0;
+
+ /* update inode block */
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ }
+ free(node_blk);
+}
+
+void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino,
+ nid_t nid, block_t newaddr)
+{
+ struct f2fs_nat_block *nat_block;
+ pgoff_t block_addr;
+ int entry_off;
+ int ret;
+
+ nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+ ASSERT(nat_block);
+
+ entry_off = nid % NAT_ENTRY_PER_BLOCK;
+ block_addr = current_nat_addr(sbi, nid);
+
+ 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);
+ ASSERT(ret >= 0);
+ free(nat_block);
+}
+
void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
{
struct f2fs_nat_entry raw_nat;
@@ -933,25 +1339,28 @@
{
struct sit_info *sit_i = SIT_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
- struct f2fs_summary_block *sum = curseg->sum_blk;
- unsigned int segno;
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
+ struct seg_entry *se;
+ struct f2fs_sit_entry sit;
+ unsigned int i, segno;
for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
- struct seg_entry *se = &sit_i->sentries[segno];
+ se = &sit_i->sentries[segno];
struct f2fs_sit_block *sit_blk;
- struct f2fs_sit_entry sit;
- int i;
- for (i = 0; i < sits_in_cursum(sum); i++) {
- if (le32_to_cpu(segno_in_journal(sum, i)) == segno) {
- sit = sit_in_journal(sum, i);
- goto got_it;
- }
- }
sit_blk = get_current_sit_page(sbi, segno);
sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
free(sit_blk);
-got_it:
+
+ check_block_count(sbi, segno, &sit);
+ seg_info_from_raw_sit(se, &sit);
+ }
+
+ for (i = 0; i < sits_in_cursum(journal); i++) {
+ segno = le32_to_cpu(segno_in_journal(journal, i));
+ se = &sit_i->sentries[segno];
+ sit = sit_in_journal(journal, i);
+
check_block_count(sbi, segno, &sit);
seg_info_from_raw_sit(se, &sit);
}
@@ -960,8 +1369,8 @@
int build_segment_manager(struct f2fs_sb_info *sbi)
{
- struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
struct f2fs_sm_info *sm_info;
sm_info = malloc(sizeof(struct f2fs_sm_info));
@@ -970,13 +1379,13 @@
/* init sm info */
sbi->sm_info = sm_info;
- sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
- sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr);
- sm_info->segment_count = le32_to_cpu(raw_super->segment_count);
- sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count);
- sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
- sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main);
- sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
+ sm_info->seg0_blkaddr = get_sb(segment0_blkaddr);
+ sm_info->main_blkaddr = get_sb(main_blkaddr);
+ sm_info->segment_count = get_sb(segment_count);
+ sm_info->reserved_segments = get_cp(rsvd_segment_count);
+ sm_info->ovp_segments = get_cp(overprov_segment_count);
+ sm_info->main_segments = get_sb(segment_count_main);
+ sm_info->ssa_blkaddr = get_sb(ssa_blkaddr);
build_sit_info(sbi);
@@ -999,6 +1408,7 @@
fsck->sit_area_bitmap_sz = sm_i->main_segments * SIT_VBLOCK_MAP_SIZE;
fsck->sit_area_bitmap = calloc(1, fsck->sit_area_bitmap_sz);
+ ASSERT(fsck->sit_area_bitmap);
ptr = fsck->sit_area_bitmap;
ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz);
@@ -1010,12 +1420,12 @@
ptr += SIT_VBLOCK_MAP_SIZE;
if (se->valid_blocks == 0x0) {
- if (sbi->ckpt->cur_node_segno[0] == segno ||
- sbi->ckpt->cur_data_segno[0] == segno ||
- sbi->ckpt->cur_node_segno[1] == segno ||
- sbi->ckpt->cur_data_segno[1] == segno ||
- sbi->ckpt->cur_node_segno[2] == segno ||
- sbi->ckpt->cur_data_segno[2] == segno) {
+ if (le32_to_cpu(sbi->ckpt->cur_node_segno[0]) == segno ||
+ le32_to_cpu(sbi->ckpt->cur_data_segno[0]) == segno ||
+ le32_to_cpu(sbi->ckpt->cur_node_segno[1]) == segno ||
+ le32_to_cpu(sbi->ckpt->cur_data_segno[1]) == segno ||
+ le32_to_cpu(sbi->ckpt->cur_node_segno[2]) == segno ||
+ le32_to_cpu(sbi->ckpt->cur_data_segno[2]) == segno) {
continue;
} else {
free_segs++;
@@ -1042,9 +1452,7 @@
char *ptr = NULL;
/* remove sit journal */
- sum->n_sits = 0;
-
- fsck->chk.free_segs = 0;
+ sum->journal.n_sits = 0;
ptr = fsck->main_area_bitmap;
@@ -1065,6 +1473,8 @@
valid_blocks += get_bits_in_byte(sit->valid_map[i]);
se = get_seg_entry(sbi, segno);
+ memcpy(se->cur_valid_map, ptr, SIT_VBLOCK_MAP_SIZE);
+ se->valid_blocks = valid_blocks;
type = se->type;
if (type >= NO_CHECK_TYPE) {
ASSERT_MSG("Invalide type and valid blocks=%x,%x",
@@ -1076,29 +1486,255 @@
rewrite_current_sit_page(sbi, segno, sit_blk);
free(sit_blk);
- if (valid_blocks == 0 &&
- sbi->ckpt->cur_node_segno[0] != segno &&
- sbi->ckpt->cur_data_segno[0] != segno &&
- sbi->ckpt->cur_node_segno[1] != segno &&
- sbi->ckpt->cur_data_segno[1] != segno &&
- sbi->ckpt->cur_node_segno[2] != segno &&
- sbi->ckpt->cur_data_segno[2] != segno)
- fsck->chk.free_segs++;
-
ptr += SIT_VBLOCK_MAP_SIZE;
}
}
+static int flush_sit_journal_entries(struct f2fs_sb_info *sbi)
+{
+ struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
+ struct sit_info *sit_i = SIT_I(sbi);
+ unsigned int segno;
+ int i;
+
+ for (i = 0; i < sits_in_cursum(journal); i++) {
+ struct f2fs_sit_block *sit_blk;
+ struct f2fs_sit_entry *sit;
+ struct seg_entry *se;
+
+ segno = segno_in_journal(journal, i);
+ se = get_seg_entry(sbi, segno);
+
+ sit_blk = get_current_sit_page(sbi, segno);
+ sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
+
+ memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+ sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+ se->valid_blocks);
+ sit->mtime = cpu_to_le64(se->mtime);
+
+ rewrite_current_sit_page(sbi, segno, sit_blk);
+ free(sit_blk);
+ }
+
+ journal->n_sits = 0;
+ return i;
+}
+
+static int flush_nat_journal_entries(struct f2fs_sb_info *sbi)
+{
+ struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
+ struct f2fs_nat_block *nat_block;
+ pgoff_t block_addr;
+ int entry_off;
+ nid_t nid;
+ int ret;
+ int i = 0;
+
+ nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+ ASSERT(nat_block);
+next:
+ if (i >= nats_in_cursum(journal)) {
+ free(nat_block);
+ journal->n_nats = 0;
+ return i;
+ }
+
+ nid = le32_to_cpu(nid_in_journal(journal, i));
+
+ entry_off = nid % NAT_ENTRY_PER_BLOCK;
+ block_addr = current_nat_addr(sbi, nid);
+
+ ret = dev_read_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+
+ memcpy(&nat_block->entries[entry_off], &nat_in_journal(journal, i),
+ sizeof(struct f2fs_nat_entry));
+
+ ret = dev_write_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+ i++;
+ goto next;
+}
+
+void flush_journal_entries(struct f2fs_sb_info *sbi)
+{
+ int n_nats = flush_nat_journal_entries(sbi);
+ int n_sits = flush_sit_journal_entries(sbi);
+
+ if (n_nats || n_sits)
+ write_checkpoint(sbi);
+}
+
+void flush_sit_entries(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ struct sit_info *sit_i = SIT_I(sbi);
+ unsigned int segno = 0;
+ u32 free_segs = 0;
+
+ /* update free segments */
+ for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
+ struct f2fs_sit_block *sit_blk;
+ struct f2fs_sit_entry *sit;
+ struct seg_entry *se;
+
+ se = get_seg_entry(sbi, segno);
+
+ if (!se->dirty)
+ continue;
+
+ sit_blk = get_current_sit_page(sbi, segno);
+ sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)];
+ memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+ sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+ se->valid_blocks);
+ rewrite_current_sit_page(sbi, segno, sit_blk);
+ free(sit_blk);
+
+ if (se->valid_blocks == 0x0 &&
+ !IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE))
+ free_segs++;
+ }
+
+ set_cp(free_segment_count, free_segs);
+}
+
+int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct seg_entry *se;
+ u32 segno;
+ u64 offset;
+ int not_enough = 0;
+ u64 end_blkaddr = (get_sb(segment_count_main) <<
+ get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr);
+
+ if (get_free_segments(sbi) <= SM_I(sbi)->reserved_segments + 1)
+ not_enough = 1;
+
+ while (*to >= SM_I(sbi)->main_blkaddr && *to < end_blkaddr) {
+ segno = GET_SEGNO(sbi, *to);
+ offset = OFFSET_IN_SEG(sbi, *to);
+
+ se = get_seg_entry(sbi, segno);
+
+ if (se->valid_blocks == sbi->blocks_per_seg ||
+ IS_CUR_SEGNO(sbi, segno, type)) {
+ *to = left ? START_BLOCK(sbi, segno) - 1:
+ START_BLOCK(sbi, segno + 1);
+ continue;
+ }
+
+ if (se->valid_blocks == 0 && not_enough) {
+ *to = left ? START_BLOCK(sbi, segno) - 1:
+ START_BLOCK(sbi, segno + 1);
+ continue;
+ }
+
+ if (se->valid_blocks == 0 && !(segno % sbi->segs_per_sec)) {
+ struct seg_entry *se2;
+ unsigned int i;
+
+ for (i = 1; i < sbi->segs_per_sec; i++) {
+ se2 = get_seg_entry(sbi, segno + i);
+ if (se2->valid_blocks)
+ break;
+ }
+ if (i == sbi->segs_per_sec)
+ return 0;
+ }
+
+ if (se->type == type &&
+ !f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+ return 0;
+
+ *to = left ? *to - 1: *to + 1;
+ }
+ return -1;
+}
+
+void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
+{
+ int i, ret;
+
+ /* update summary blocks having nullified journal entries */
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ struct curseg_info *curseg = CURSEG_I(sbi, i);
+ struct f2fs_summary_block buf;
+ u32 old_segno;
+ u64 ssa_blk, to;
+
+ /* update original SSA too */
+ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+ ret = dev_write_block(curseg->sum_blk, ssa_blk);
+ ASSERT(ret >= 0);
+
+ to = from;
+ ret = find_next_free_block(sbi, &to, 0, i);
+ ASSERT(ret == 0);
+
+ old_segno = curseg->segno;
+ curseg->segno = GET_SEGNO(sbi, to);
+ curseg->next_blkoff = OFFSET_IN_SEG(sbi, to);
+ curseg->alloc_type = SSR;
+
+ /* update new segno */
+ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+ ret = dev_read_block(&buf, ssa_blk);
+ ASSERT(ret >= 0);
+
+ memcpy(curseg->sum_blk, &buf, SUM_ENTRIES_SIZE);
+
+ /* update se->types */
+ reset_curseg(sbi, i);
+
+ DBG(1, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
+ i, old_segno, curseg->segno, from);
+ }
+}
+
+void zero_journal_entries(struct f2fs_sb_info *sbi)
+{
+ int i;
+
+ for (i = 0; i < NO_CHECK_TYPE; i++)
+ CURSEG_I(sbi, i)->sum_blk->journal.n_nats = 0;
+}
+
+void write_curseg_info(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ int i;
+
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ cp->alloc_type[i] = CURSEG_I(sbi, i)->alloc_type;
+ if (i < CURSEG_HOT_NODE) {
+ set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno);
+ set_cp(cur_data_blkoff[i],
+ CURSEG_I(sbi, i)->next_blkoff);
+ } else {
+ int n = i - CURSEG_HOT_NODE;
+
+ set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno);
+ set_cp(cur_node_blkoff[n],
+ CURSEG_I(sbi, i)->next_blkoff);
+ }
+ }
+}
+
int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid,
struct f2fs_nat_entry *raw_nat)
{
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
- struct f2fs_summary_block *sum = curseg->sum_blk;
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
int i = 0;
- for (i = 0; i < nats_in_cursum(sum); i++) {
- if (le32_to_cpu(nid_in_journal(sum, i)) == nid) {
- memcpy(raw_nat, &nat_in_journal(sum, i),
+ for (i = 0; i < nats_in_cursum(journal); i++) {
+ if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
+ memcpy(raw_nat, &nat_in_journal(journal, i),
sizeof(struct f2fs_nat_entry));
DBG(3, "==> Found nid [0x%x] in nat cache\n", nid);
return i;
@@ -1110,36 +1746,27 @@
void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
{
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
- struct f2fs_summary_block *sum = curseg->sum_blk;
- struct f2fs_nm_info *nm_i = NM_I(sbi);
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
struct f2fs_nat_block *nat_block;
- pgoff_t block_off;
pgoff_t block_addr;
- int seg_off, entry_off;
+ int entry_off;
int ret;
int i = 0;
/* check in journal */
- for (i = 0; i < nats_in_cursum(sum); i++) {
- if (le32_to_cpu(nid_in_journal(sum, i)) == nid) {
- memset(&nat_in_journal(sum, i), 0,
+ for (i = 0; i < nats_in_cursum(journal); i++) {
+ if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
+ memset(&nat_in_journal(journal, i), 0,
sizeof(struct f2fs_nat_entry));
FIX_MSG("Remove nid [0x%x] in nat journal\n", nid);
return;
}
}
nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
+ ASSERT(nat_block);
- block_off = nid / NAT_ENTRY_PER_BLOCK;
entry_off = nid % NAT_ENTRY_PER_BLOCK;
-
- 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;
+ block_addr = current_nat_addr(sbi, nid);
ret = dev_read_block(nat_block, block_addr);
ASSERT(ret >= 0);
@@ -1152,12 +1779,71 @@
free(nat_block);
}
+void write_checkpoint(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ block_t orphan_blks = 0;
+ unsigned long long cp_blk_no;
+ u32 flags = CP_UMOUNT_FLAG;
+ int i, ret;
+ u_int32_t crc = 0;
+
+ if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) {
+ orphan_blks = __start_sum_addr(sbi) - 1;
+ flags |= CP_ORPHAN_PRESENT_FLAG;
+ }
+
+ 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);
+ *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+ cp_blk_no = get_sb(cp_blkaddr);
+ if (sbi->cur_cp == 2)
+ cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
+ /* write the first cp */
+ ret = dev_write_block(cp, cp_blk_no++);
+ ASSERT(ret >= 0);
+
+ /* skip payload */
+ cp_blk_no += get_sb(cp_payload);
+ /* skip orphan blocks */
+ cp_blk_no += orphan_blks;
+
+ /* update summary blocks having nullified journal entries */
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ struct curseg_info *curseg = CURSEG_I(sbi, i);
+ u64 ssa_blk;
+
+ ret = dev_write_block(curseg->sum_blk, cp_blk_no++);
+ ASSERT(ret >= 0);
+
+ /* update original SSA too */
+ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+ ret = dev_write_block(curseg->sum_blk, ssa_blk);
+ ASSERT(ret >= 0);
+ }
+
+ /* write the last cp */
+ ret = dev_write_block(cp, cp_blk_no++);
+ ASSERT(ret >= 0);
+}
+
void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
{
+ struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+ struct f2fs_journal *journal = &curseg->sum_blk->journal;
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
- struct f2fs_super_block *raw_sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_nat_block *nat_block;
+ struct node_info ni;
u32 nid, nr_nat_blks;
pgoff_t block_off;
pgoff_t block_addr;
@@ -1169,13 +1855,17 @@
ASSERT(nat_block);
/* Alloc & build nat entry bitmap */
- nr_nat_blks = (le32_to_cpu(raw_sb->segment_count_nat) / 2) <<
- sbi->log_blocks_per_seg;
+ nr_nat_blks = (get_sb(segment_count_nat) / 2) <<
+ sbi->log_blocks_per_seg;
fsck->nr_nat_entries = nr_nat_blks * NAT_ENTRY_PER_BLOCK;
fsck->nat_area_bitmap_sz = (fsck->nr_nat_entries + 7) / 8;
fsck->nat_area_bitmap = calloc(fsck->nat_area_bitmap_sz, 1);
- ASSERT(fsck->nat_area_bitmap != NULL);
+ ASSERT(fsck->nat_area_bitmap);
+
+ fsck->entries = calloc(sizeof(struct f2fs_nat_entry),
+ fsck->nr_nat_entries);
+ ASSERT(fsck->entries);
for (block_off = 0; block_off < nr_nat_blks; block_off++) {
@@ -1192,40 +1882,88 @@
nid = block_off * NAT_ENTRY_PER_BLOCK;
for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
- struct f2fs_nat_entry raw_nat;
- struct node_info ni;
ni.nid = nid + i;
if ((nid + i) == F2FS_NODE_INO(sbi) ||
(nid + i) == F2FS_META_INO(sbi)) {
- ASSERT(nat_block->entries[i].block_addr != 0x0);
+ /* block_addr of node/meta inode should be 0x1 */
+ if (le32_to_cpu(nat_block->entries[i].block_addr) != 0x1) {
+ FIX_MSG("ino: 0x%x node/meta inode, block_addr= 0x%x -> 0x1",
+ nid + i, le32_to_cpu(nat_block->entries[i].block_addr));
+ nat_block->entries[i].block_addr = cpu_to_le32(0x1);
+ ret = dev_write_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+ }
continue;
}
- if (lookup_nat_in_journal(sbi, nid + i,
- &raw_nat) >= 0) {
- node_info_from_raw_nat(&ni, &raw_nat);
- if (ni.blk_addr != 0x0) {
- f2fs_set_bit(nid + i,
- fsck->nat_area_bitmap);
- fsck->chk.valid_nat_entry_cnt++;
- DBG(3, "nid[0x%x] in nat cache\n",
- nid + i);
- }
- } else {
- node_info_from_raw_nat(&ni,
- &nat_block->entries[i]);
- if (ni.blk_addr == 0)
- continue;
- ASSERT(nid + i != 0x0);
-
- DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n",
- nid + i, ni.blk_addr, ni.ino);
- f2fs_set_bit(nid + i, fsck->nat_area_bitmap);
- fsck->chk.valid_nat_entry_cnt++;
+ node_info_from_raw_nat(&ni, &nat_block->entries[i]);
+ if (ni.blk_addr == 0x0)
+ continue;
+ if (ni.ino == 0x0) {
+ ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]"
+ " is invalid\n", ni.ino, ni.blk_addr);
}
+ if (ni.ino == (nid + i)) {
+ fsck->nat_valid_inode_cnt++;
+ DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino);
+ }
+ if (nid + i == 0) {
+ /*
+ * nat entry [0] must be null. If
+ * it is corrupted, set its bit in
+ * nat_area_bitmap, fsck_verify will
+ * nullify it
+ */
+ ASSERT_MSG("Invalid nat entry[0]: "
+ "blk_addr[0x%x]\n", ni.blk_addr);
+ c.fix_on = 1;
+ fsck->chk.valid_nat_entry_cnt--;
+ }
+
+ DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n",
+ nid + i, ni.blk_addr, ni.ino);
+ f2fs_set_bit(nid + i, fsck->nat_area_bitmap);
+ fsck->chk.valid_nat_entry_cnt++;
+
+ fsck->entries[nid + i] = nat_block->entries[i];
}
}
+
+ /* Traverse nat journal, update the corresponding entries */
+ for (i = 0; i < nats_in_cursum(journal); i++) {
+ struct f2fs_nat_entry raw_nat;
+ nid = le32_to_cpu(nid_in_journal(journal, i));
+ ni.nid = nid;
+
+ DBG(3, "==> Found nid [0x%x] in nat cache, update it\n", nid);
+
+ /* Clear the original bit and count */
+ if (fsck->entries[nid].block_addr != 0x0) {
+ fsck->chk.valid_nat_entry_cnt--;
+ f2fs_clear_bit(nid, fsck->nat_area_bitmap);
+ if (fsck->entries[nid].ino == nid)
+ fsck->nat_valid_inode_cnt--;
+ }
+
+ /* Use nat entries in journal */
+ memcpy(&raw_nat, &nat_in_journal(journal, i),
+ sizeof(struct f2fs_nat_entry));
+ node_info_from_raw_nat(&ni, &raw_nat);
+ if (ni.blk_addr != 0x0) {
+ if (ni.ino == 0x0)
+ ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]"
+ " is invalid\n", ni.ino, ni.blk_addr);
+ if (ni.ino == nid) {
+ fsck->nat_valid_inode_cnt++;
+ DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino);
+ }
+ f2fs_set_bit(nid, fsck->nat_area_bitmap);
+ fsck->chk.valid_nat_entry_cnt++;
+ DBG(3, "nid[0x%x] in nat cache\n", nid);
+ }
+ fsck->entries[nid] = raw_nat;
+ }
free(nat_block);
DBG(1, "valid nat entries (block_addr != 0x0) [0x%8x : %u]\n",
@@ -1233,8 +1971,43 @@
fsck->chk.valid_nat_entry_cnt);
}
+static int check_sector_size(struct f2fs_super_block *sb)
+{
+ int index;
+ u_int32_t log_sectorsize, log_sectors_per_block;
+ u_int8_t *zero_buff;
+
+ log_sectorsize = log_base_2(c.sector_size);
+ log_sectors_per_block = log_base_2(c.sectors_per_blk);
+
+ if (log_sectorsize == get_sb(log_sectorsize) &&
+ log_sectors_per_block == get_sb(log_sectors_per_block))
+ return 0;
+
+ zero_buff = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(zero_buff);
+
+ set_sb(log_sectorsize, log_sectorsize);
+ set_sb(log_sectors_per_block, log_sectors_per_block);
+
+ memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb));
+ DBG(1, "\tWriting super block, at offset 0x%08x\n", 0);
+ for (index = 0; index < 2; index++) {
+ if (dev_write(zero_buff, index * F2FS_BLKSIZE, F2FS_BLKSIZE)) {
+ MSG(1, "\tError: Failed while writing supe_blk "
+ "on disk!!! index : %d\n", index);
+ free(zero_buff);
+ return -1;
+ }
+ }
+
+ free(zero_buff);
+ return 0;
+}
+
int f2fs_do_mount(struct f2fs_sb_info *sbi)
{
+ struct f2fs_checkpoint *cp = NULL;
int ret;
sbi->active_logs = NR_CURSEG_TYPE;
@@ -1245,7 +2018,11 @@
return -1;
}
- print_raw_sb_info(sbi);
+ ret = check_sector_size(sbi->raw_super);
+ if (ret)
+ return -1;
+
+ print_raw_sb_info(F2FS_RAW_SUPER(sbi));
init_sb_info(sbi);
@@ -1259,26 +2036,25 @@
ERR_MSG("Checkpoint is polluted\n");
return -1;
}
+ cp = F2FS_CKPT(sbi);
print_ckpt_info(sbi);
- if (config.auto_fix) {
- u32 flag = le32_to_cpu(sbi->ckpt->ckpt_flags);
+ if (c.auto_fix || c.preen_mode) {
+ u32 flag = get_cp(ckpt_flags);
if (flag & CP_FSCK_FLAG)
- config.fix_on = 1;
- else
+ c.fix_on = 1;
+ else if (!c.preen_mode)
return 1;
}
- config.bug_on = 0;
+ c.bug_on = 0;
- sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count);
- sbi->total_valid_inode_count =
- le32_to_cpu(sbi->ckpt->valid_inode_count);
- sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count);
- sbi->total_valid_block_count =
- le64_to_cpu(sbi->ckpt->valid_block_count);
+ sbi->total_valid_node_count = get_cp(valid_node_count);
+ sbi->total_valid_inode_count = get_cp(valid_inode_count);
+ sbi->user_block_count = get_cp(user_block_count);
+ sbi->total_valid_block_count = get_cp(valid_block_count);
sbi->last_valid_block_count = sbi->total_valid_block_count;
sbi->alloc_valid_block_count = 0;
@@ -1288,7 +2064,7 @@
}
if (build_node_manager(sbi)) {
- ERR_MSG("build_segment_manager failed\n");
+ ERR_MSG("build_node_manager failed\n");
return -1;
}
@@ -1303,6 +2079,8 @@
unsigned int i;
/* free nm_info */
+ if (c.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
index 0000000..fe923e5
--- /dev/null
+++ b/fsck/node.c
@@ -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(le16_to_cpu(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
index 0000000..721e5b7
--- /dev/null
+++ b/fsck/node.h
@@ -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/resize.c b/fsck/resize.c
new file mode 100644
index 0000000..29cb1c0
--- /dev/null
+++ b/fsck/resize.c
@@ -0,0 +1,601 @@
+/**
+ * resize.c
+ *
+ * Copyright (c) 2015 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"
+
+static int get_new_sb(struct f2fs_super_block *sb)
+{
+ u_int32_t zone_size_bytes, zone_align_start_offset;
+ u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
+ u_int32_t sit_segments, diff, total_meta_segments;
+ u_int32_t total_valid_blks_available;
+ u_int32_t sit_bitmap_size, max_sit_bitmap_size;
+ u_int32_t max_nat_bitmap_size, max_nat_segments;
+ u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) +
+ get_sb(log_blocks_per_seg));
+ u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg);
+ u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone);
+
+ set_sb(block_count, c.target_sectors >>
+ get_sb(log_sectors_per_block));
+
+ zone_size_bytes = segment_size_bytes * segs_per_zone;
+ zone_align_start_offset =
+ (c.start_sector * c.sector_size +
+ 2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
+ zone_size_bytes * zone_size_bytes -
+ c.start_sector * c.sector_size;
+
+ set_sb(segment_count, (c.target_sectors * c.sector_size -
+ zone_align_start_offset) / segment_size_bytes /
+ c.segs_per_sec * c.segs_per_sec);
+
+ blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
+ sit_segments = SEG_ALIGN(blocks_for_sit);
+ set_sb(segment_count_sit, sit_segments * 2);
+ set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
+ get_sb(segment_count_sit) * blks_per_seg);
+
+ total_valid_blks_available = (get_sb(segment_count) -
+ (get_sb(segment_count_ckpt) +
+ get_sb(segment_count_sit))) * blks_per_seg;
+ blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+ set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
+
+ sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
+ get_sb(log_blocks_per_seg)) / 8;
+ if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE)
+ max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE;
+ else
+ max_sit_bitmap_size = sit_bitmap_size;
+
+ /*
+ * It should be reserved minimum 1 segment for nat.
+ * When sit is too large, we should expand cp area. It requires more pages for cp.
+ */
+ if (max_sit_bitmap_size >
+ (CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
+ max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
+ set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
+ } else {
+ max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
+ - max_sit_bitmap_size;
+ set_sb(cp_payload, 0);
+ }
+
+ max_nat_segments = (max_nat_bitmap_size * 8) >>
+ get_sb(log_blocks_per_seg);
+
+ if (get_sb(segment_count_nat) > max_nat_segments)
+ set_sb(segment_count_nat, max_nat_segments);
+
+ set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
+
+ set_sb(ssa_blkaddr, get_sb(nat_blkaddr) +
+ get_sb(segment_count_nat) * blks_per_seg);
+
+ total_valid_blks_available = (get_sb(segment_count) -
+ (get_sb(segment_count_ckpt) +
+ get_sb(segment_count_sit) +
+ get_sb(segment_count_nat))) * blks_per_seg;
+
+ blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1;
+
+ set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
+
+ total_meta_segments = get_sb(segment_count_ckpt) +
+ get_sb(segment_count_sit) +
+ get_sb(segment_count_nat) +
+ get_sb(segment_count_ssa);
+
+ diff = total_meta_segments % segs_per_zone;
+ if (diff)
+ set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
+ (segs_per_zone - diff));
+
+ set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
+ blks_per_seg);
+
+ set_sb(segment_count_main, get_sb(segment_count) -
+ (get_sb(segment_count_ckpt) +
+ get_sb(segment_count_sit) +
+ get_sb(segment_count_nat) +
+ get_sb(segment_count_ssa)));
+
+ set_sb(section_count, get_sb(segment_count_main) /
+ get_sb(segs_per_sec));
+
+ set_sb(segment_count_main, get_sb(section_count) *
+ get_sb(segs_per_sec));
+
+ /* Let's determine the best reserved and overprovisioned space */
+ c.new_overprovision = get_best_overprovision(sb);
+ c.new_reserved_segments =
+ (2 * (100 / c.new_overprovision + 1) + 6) *
+ get_sb(segs_per_sec);
+
+ if ((get_sb(segment_count_main) - 2) < c.new_reserved_segments ||
+ get_sb(segment_count_main) * blks_per_seg >
+ get_sb(block_count)) {
+ MSG(0, "\tError: Device size is not sufficient for F2FS volume, "
+ "more segment needed =%u",
+ c.new_reserved_segments -
+ (get_sb(segment_count_main) - 2));
+ return -1;
+ }
+ return 0;
+}
+
+static void migrate_main(struct f2fs_sb_info *sbi, unsigned int offset)
+{
+ void *raw = calloc(BLOCK_SZ, 1);
+ struct seg_entry *se;
+ block_t from, to;
+ int i, j, ret;
+ struct f2fs_summary sum;
+
+ ASSERT(raw != NULL);
+
+ for (i = TOTAL_SEGS(sbi) - 1; i >= 0; i--) {
+ se = get_seg_entry(sbi, i);
+ if (!se->valid_blocks)
+ continue;
+
+ for (j = sbi->blocks_per_seg - 1; j >= 0; j--) {
+ if (!f2fs_test_bit(j, (const char *)se->cur_valid_map))
+ continue;
+
+ from = START_BLOCK(sbi, i) + j;
+ ret = dev_read_block(raw, from);
+ ASSERT(ret >= 0);
+
+ to = from + offset;
+ ret = dev_write_block(raw, to);
+ ASSERT(ret >= 0);
+
+ get_sum_entry(sbi, from, &sum);
+
+ if (IS_DATASEG(se->type))
+ update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
+ le16_to_cpu(sum.ofs_in_node), to);
+ else
+ update_nat_blkaddr(sbi, 0,
+ le32_to_cpu(sum.nid), to);
+ }
+ }
+ free(raw);
+ DBG(0, "Info: Done to migrate Main area: main_blkaddr = 0x%x -> 0x%x\n",
+ START_BLOCK(sbi, 0),
+ START_BLOCK(sbi, 0) + offset);
+}
+
+static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno,
+ block_t new_sum_blk_addr)
+{
+ struct f2fs_summary_block *sum_blk;
+ int type;
+
+ sum_blk = get_sum_block(sbi, segno, &type);
+ if (type < SEG_TYPE_MAX) {
+ int ret;
+
+ ret = dev_write_block(sum_blk, new_sum_blk_addr);
+ ASSERT(ret >= 0);
+ DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
+ type, segno, GET_SUM_BLKADDR(sbi, segno),
+ IS_SUM_NODE_SEG(sum_blk->footer),
+ new_sum_blk_addr);
+ }
+ if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+ type == SEG_TYPE_MAX) {
+ free(sum_blk);
+ }
+ DBG(1, "Info: Done to migrate SSA blocks\n");
+}
+
+static void migrate_ssa(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *new_sb, unsigned int offset)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ block_t old_sum_blkaddr = get_sb(ssa_blkaddr);
+ block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
+ block_t end_sum_blkaddr = get_newsb(main_blkaddr);
+ block_t expand_sum_blkaddr = new_sum_blkaddr +
+ TOTAL_SEGS(sbi) - offset;
+ block_t blkaddr;
+ int ret;
+ void *zero_block = calloc(BLOCK_SZ, 1);
+ ASSERT(zero_block);
+
+ if (offset && new_sum_blkaddr < old_sum_blkaddr + offset) {
+ blkaddr = new_sum_blkaddr;
+ while (blkaddr < end_sum_blkaddr) {
+ if (blkaddr < expand_sum_blkaddr) {
+ move_ssa(sbi, offset++, blkaddr++);
+ } else {
+ ret = dev_write_block(zero_block, blkaddr++);
+ ASSERT(ret >=0);
+ }
+ }
+ } else {
+ blkaddr = end_sum_blkaddr - 1;
+ offset = TOTAL_SEGS(sbi) - 1;
+ while (blkaddr >= new_sum_blkaddr) {
+ if (blkaddr >= expand_sum_blkaddr) {
+ ret = dev_write_block(zero_block, blkaddr--);
+ ASSERT(ret >=0);
+ } else {
+ move_ssa(sbi, offset--, blkaddr--);
+ }
+ }
+ }
+
+ DBG(0, "Info: Done to migrate SSA blocks: sum_blkaddr = 0x%x -> 0x%x\n",
+ old_sum_blkaddr, new_sum_blkaddr);
+ free(zero_block);
+}
+
+static int shrink_nats(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *new_sb)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+ unsigned int nat_blocks;
+ void *nat_block, *zero_block;
+ int nid, ret, new_max_nid;
+ pgoff_t block_off;
+ pgoff_t block_addr;
+ int seg_off;
+
+ nat_block = malloc(BLOCK_SZ);
+ ASSERT(nat_block);
+ zero_block = calloc(BLOCK_SZ, 1);
+ ASSERT(zero_block);
+
+ nat_blocks = get_newsb(segment_count_nat) >> 1;
+ nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+ new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+ for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) {
+ block_off = nid / NAT_ENTRY_PER_BLOCK;
+ seg_off = block_off >> sbi->log_blocks_per_seg;
+ block_addr = (pgoff_t)(old_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;
+
+ ret = dev_read_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+
+ if (memcmp(zero_block, nat_block, BLOCK_SZ)) {
+ ret = -1;
+ goto not_avail;
+ }
+ }
+ ret = 0;
+ nm_i->max_nid = new_max_nid;
+not_avail:
+ free(nat_block);
+ free(zero_block);
+ return ret;
+}
+
+static void migrate_nat(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *new_sb)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+ block_t new_nat_blkaddr = get_newsb(nat_blkaddr);
+ unsigned int nat_blocks;
+ void *nat_block;
+ int nid, ret, new_max_nid;
+ pgoff_t block_off;
+ pgoff_t block_addr;
+ int seg_off;
+
+ nat_block = malloc(BLOCK_SZ);
+ ASSERT(nat_block);
+
+ for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
+ block_off = nid / NAT_ENTRY_PER_BLOCK;
+ seg_off = block_off >> sbi->log_blocks_per_seg;
+ block_addr = (pgoff_t)(old_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;
+
+ ret = dev_read_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+
+ block_addr = (pgoff_t)(new_nat_blkaddr +
+ (seg_off << sbi->log_blocks_per_seg << 1) +
+ (block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+ /* new bitmap should be zeros */
+ ret = dev_write_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+ }
+ /* zero out newly assigned nids */
+ memset(nat_block, 0, BLOCK_SZ);
+ nat_blocks = get_newsb(segment_count_nat) >> 1;
+ nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+ new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+ DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n",
+ old_nat_blkaddr, new_nat_blkaddr,
+ get_sb(segment_count_nat),
+ get_newsb(segment_count_nat));
+
+ for (nid = nm_i->max_nid; nid < new_max_nid;
+ nid += NAT_ENTRY_PER_BLOCK) {
+ block_off = nid / NAT_ENTRY_PER_BLOCK;
+ seg_off = block_off >> sbi->log_blocks_per_seg;
+ block_addr = (pgoff_t)(new_nat_blkaddr +
+ (seg_off << sbi->log_blocks_per_seg << 1) +
+ (block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+ ret = dev_write_block(nat_block, block_addr);
+ ASSERT(ret >= 0);
+ DBG(3, "Write NAT: %lx\n", block_addr);
+ }
+ DBG(0, "Info: Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n",
+ old_nat_blkaddr, new_nat_blkaddr);
+}
+
+static void migrate_sit(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *new_sb, unsigned int offset)
+{
+ struct sit_info *sit_i = SIT_I(sbi);
+ unsigned int ofs = 0, pre_ofs = 0;
+ unsigned int segno, index;
+ struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
+ block_t sit_blks = get_newsb(segment_count_sit) <<
+ (sbi->log_blocks_per_seg - 1);
+ struct seg_entry *se;
+ block_t blk_addr = 0;
+ int ret;
+
+ ASSERT(sit_blk);
+
+ /* initialize with zeros */
+ for (index = 0; index < sit_blks; index++) {
+ ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index);
+ ASSERT(ret >= 0);
+ DBG(3, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
+ }
+
+ for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
+ struct f2fs_sit_entry *sit;
+
+ se = get_seg_entry(sbi, segno);
+ if (segno < offset) {
+ ASSERT(se->valid_blocks == 0);
+ continue;
+ }
+
+ ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset);
+
+ if (ofs != pre_ofs) {
+ blk_addr = get_newsb(sit_blkaddr) + pre_ofs;
+ ret = dev_write_block(sit_blk, blk_addr);
+ ASSERT(ret >= 0);
+ DBG(1, "Write valid sit: %x\n", blk_addr);
+
+ pre_ofs = ofs;
+ memset(sit_blk, 0, BLOCK_SZ);
+ }
+
+ sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)];
+ memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
+ sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) |
+ se->valid_blocks);
+ }
+ blk_addr = get_newsb(sit_blkaddr) + ofs;
+ ret = dev_write_block(sit_blk, blk_addr);
+ DBG(1, "Write valid sit: %x\n", blk_addr);
+ ASSERT(ret >= 0);
+
+ free(sit_blk);
+ DBG(0, "Info: Done to restore new SIT blocks: 0x%x\n",
+ get_newsb(sit_blkaddr));
+}
+
+static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *new_sb, unsigned int offset)
+{
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ struct f2fs_checkpoint *new_cp;
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ unsigned int free_segment_count, new_segment_count;
+ block_t new_cp_blks = 1 + get_newsb(cp_payload);
+ block_t orphan_blks = 0;
+ block_t new_cp_blk_no, old_cp_blk_no;
+ u_int32_t crc = 0;
+ void *buf;
+ int i, ret;
+
+ new_cp = calloc(new_cp_blks * BLOCK_SZ, 1);
+ ASSERT(new_cp);
+
+ buf = malloc(BLOCK_SZ);
+ ASSERT(buf);
+
+ /* ovp / free segments */
+ set_cp(rsvd_segment_count, c.new_reserved_segments);
+ set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
+ get_cp(rsvd_segment_count)) *
+ c.new_overprovision / 100);
+ set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
+ get_cp(rsvd_segment_count));
+
+ free_segment_count = get_free_segments(sbi);
+ new_segment_count = get_newsb(segment_count_main) -
+ get_sb(segment_count_main);
+
+ set_cp(free_segment_count, free_segment_count + new_segment_count);
+ set_cp(user_block_count, ((get_newsb(segment_count_main) -
+ get_cp(overprov_segment_count)) * c.blks_per_seg));
+
+ if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
+ orphan_blks = __start_sum_addr(sbi) - 1;
+
+ set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
+ set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
+
+ /* cur->segno - offset */
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ if (i < CURSEG_HOT_NODE) {
+ set_cp(cur_data_segno[i],
+ CURSEG_I(sbi, i)->segno - offset);
+ } else {
+ int n = i - CURSEG_HOT_NODE;
+
+ set_cp(cur_node_segno[n],
+ CURSEG_I(sbi, i)->segno - offset);
+ }
+ }
+
+ /* sit / nat ver bitmap bytesize */
+ set_cp(sit_ver_bitmap_bytesize,
+ ((get_newsb(segment_count_sit) / 2) <<
+ get_newsb(log_blocks_per_seg)) / 8);
+ set_cp(nat_ver_bitmap_bytesize,
+ ((get_newsb(segment_count_nat) / 2) <<
+ get_newsb(log_blocks_per_seg)) / 8);
+
+ memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
+ (unsigned char *)cp);
+
+ crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
+ *((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+ /* Write a new checkpoint in the other set */
+ new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
+ if (sbi->cur_cp == 2)
+ old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+ else
+ new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
+ /* write first cp */
+ ret = dev_write_block(new_cp, new_cp_blk_no++);
+ ASSERT(ret >= 0);
+
+ memset(buf, 0, BLOCK_SZ);
+ for (i = 0; i < get_newsb(cp_payload); i++) {
+ ret = dev_write_block(buf, new_cp_blk_no++);
+ ASSERT(ret >= 0);
+ }
+
+ for (i = 0; i < orphan_blks; i++) {
+ block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
+
+ ret = dev_read_block(buf, orphan_blk_no++);
+ ASSERT(ret >= 0);
+
+ ret = dev_write_block(buf, new_cp_blk_no++);
+ ASSERT(ret >= 0);
+ }
+
+ /* update summary blocks having nullified journal entries */
+ for (i = 0; i < NO_CHECK_TYPE; i++) {
+ struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+ ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
+ ASSERT(ret >= 0);
+ }
+
+ /* write the last cp */
+ ret = dev_write_block(new_cp, new_cp_blk_no++);
+ ASSERT(ret >= 0);
+
+ /* disable old checkpoint */
+ memset(buf, 0, BLOCK_SZ);
+ ret = dev_write_block(buf, old_cp_blk_no);
+ ASSERT(ret >= 0);
+
+ free(buf);
+ free(new_cp);
+ DBG(0, "Info: Done to rebuild checkpoint blocks\n");
+}
+
+static void rebuild_superblock(struct f2fs_super_block *new_sb)
+{
+ int index, ret;
+ u_int8_t *buf;
+
+ buf = calloc(BLOCK_SZ, 1);
+
+ memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
+ for (index = 0; index < 2; index++) {
+ ret = dev_write_block(buf, index);
+ ASSERT(ret >= 0);
+ }
+ free(buf);
+ DBG(0, "Info: Done to rebuild superblock\n");
+}
+
+int f2fs_resize(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_super_block new_sb_raw;
+ struct f2fs_super_block *new_sb = &new_sb_raw;
+ block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
+ unsigned int offset;
+ unsigned int offset_seg = 0;
+ int err = -1;
+
+ /* flush NAT/SIT journal entries */
+ flush_journal_entries(sbi);
+
+ memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
+ if (get_new_sb(new_sb))
+ return -1;
+
+ /* check nat availability */
+ if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
+ err = shrink_nats(sbi, new_sb);
+ if (err) {
+ MSG(0, "\tError: Failed to shrink NATs\n");
+ return err;
+ }
+ }
+
+ print_raw_sb_info(sb);
+ print_raw_sb_info(new_sb);
+
+ old_main_blkaddr = get_sb(main_blkaddr);
+ new_main_blkaddr = get_newsb(main_blkaddr);
+ offset = new_main_blkaddr - old_main_blkaddr;
+ end_blkaddr = (get_sb(segment_count_main) <<
+ get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr);
+
+ err = -EAGAIN;
+ if (new_main_blkaddr < end_blkaddr) {
+ err = f2fs_defragment(sbi, old_main_blkaddr, offset,
+ new_main_blkaddr, 0);
+ if (!err)
+ offset_seg = offset >> get_sb(log_blocks_per_seg);
+ MSG(0, "Try to do defragement: %s\n", err ? "Skip": "Done");
+ }
+ /* move whole data region */
+ if (err)
+ migrate_main(sbi, offset);
+
+ migrate_ssa(sbi, new_sb, offset_seg);
+ migrate_nat(sbi, new_sb);
+ migrate_sit(sbi, new_sb, offset_seg);
+ rebuild_checkpoint(sbi, new_sb, offset_seg);
+ rebuild_superblock(new_sb);
+ return 0;
+}
diff --git a/fsck/segment.c b/fsck/segment.c
new file mode 100644
index 0000000..6b2f6c1
--- /dev/null
+++ b/fsck/segment.c
@@ -0,0 +1,221 @@
+/**
+ * segment.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ * Hou Pengyang <houpengyang@huawei.com>
+ * Liu Shuoran <liushuoran@huawei.com>
+ * Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "fsck.h"
+#include "node.h"
+
+void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
+ struct f2fs_summary *sum, int type)
+{
+ struct seg_entry *se;
+ u64 blkaddr;
+ u64 offset;
+
+ blkaddr = SM_I(sbi)->main_blkaddr;
+
+ if (find_next_free_block(sbi, &blkaddr, 0, type)) {
+ ERR_MSG("Not enough space to allocate blocks");
+ ASSERT(0);
+ }
+
+ se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
+ offset = OFFSET_IN_SEG(sbi, blkaddr);
+ se->type = type;
+ se->valid_blocks++;
+ f2fs_set_bit(offset, (char *)se->cur_valid_map);
+ sbi->total_valid_block_count++;
+ se->dirty = 1;
+
+ /* read/write SSA */
+ *to = (block_t)blkaddr;
+ update_sum_entry(sbi, *to, sum);
+}
+
+void new_data_block(struct f2fs_sb_info *sbi, void *block,
+ struct dnode_of_data *dn, int type)
+{
+ struct f2fs_summary sum;
+ struct node_info ni;
+
+ ASSERT(dn->node_blk);
+ memset(block, 0, BLOCK_SZ);
+
+ get_node_info(sbi, dn->nid, &ni);
+ set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
+ reserve_new_block(sbi, &dn->data_blkaddr, &sum, type);
+
+ inc_inode_blocks(dn);
+ set_data_blkaddr(dn);
+}
+
+static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+ u64 count, pgoff_t offset)
+{
+ u64 start = F2FS_BYTES_TO_BLK(offset);
+ u64 len = F2FS_BYTES_TO_BLK(count);
+ u64 end_offset;
+ u64 off_in_block, len_in_block, len_already;
+ struct dnode_of_data dn = {0};
+ void *data_blk;
+ struct node_info ni;
+ struct f2fs_node *inode;
+ int ret = -1;
+
+ get_node_info(sbi, ino, &ni);
+ inode = calloc(BLOCK_SZ, 1);
+ ASSERT(inode);
+
+ ret = dev_read_block(inode, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ if (S_ISDIR(le16_to_cpu(inode->i.i_mode)) ||
+ S_ISLNK(le16_to_cpu(inode->i.i_mode)))
+ ASSERT(0);
+
+ off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1);
+ len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block;
+ len_already = 0;
+
+ /*
+ * When calculate how many blocks this 'count' stride accross,
+ * We should take offset in a block in account.
+ */
+ len = F2FS_BYTES_TO_BLK(count + off_in_block
+ + ((1 << F2FS_BLKSIZE_BITS) - 1));
+
+ data_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(data_blk);
+
+ set_new_dnode(&dn, inode, NULL, ino);
+
+ while (len) {
+ if (dn.node_blk != dn.inode_blk)
+ free(dn.node_blk);
+
+ set_new_dnode(&dn, inode, NULL, ino);
+ get_dnode_of_data(sbi, &dn, start, ALLOC_NODE);
+
+ end_offset = ADDRS_PER_PAGE(dn.node_blk);
+
+ while (dn.ofs_in_node < end_offset && len) {
+ block_t blkaddr;
+
+ blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+
+ /* A new page from WARM_DATA */
+ if (blkaddr == NULL_ADDR)
+ new_data_block(sbi, data_blk, &dn,
+ CURSEG_WARM_DATA);
+
+ /* Copy data from buffer to file */
+ ret = dev_read_block(data_blk, dn.data_blkaddr);
+ ASSERT(ret >= 0);
+
+ memcpy(data_blk + off_in_block, buffer, len_in_block);
+
+ ret = dev_write_block(data_blk, dn.data_blkaddr);
+ ASSERT(ret >= 0);
+
+ off_in_block = 0;
+ len_already += len_in_block;
+ if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS))
+ len_in_block = 1 << F2FS_BLKSIZE_BITS;
+ else
+ len_in_block = count - len_already;
+ len--;
+ start++;
+ dn.ofs_in_node++;
+ }
+ /* Update the direct node */
+ if (dn.ndirty) {
+ ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
+ ASSERT(ret >= 0);
+ }
+ }
+
+ /* Update the inode info */
+ if (le64_to_cpu(inode->i.i_size) < offset + count) {
+ inode->i.i_size = cpu_to_le64(offset + count);
+ dn.idirty = 1;
+ }
+
+ if (dn.idirty) {
+ ASSERT(inode == dn.inode_blk);
+ ret = dev_write_block(inode, ni.blk_addr);
+ ASSERT(ret >= 0);
+ }
+
+ if (dn.node_blk && dn.node_blk != dn.inode_blk)
+ free(dn.node_blk);
+ free(data_blk);
+ free(inode);
+}
+
+int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+ int fd, n;
+ pgoff_t off = 0;
+ char buffer[BLOCK_SZ];
+
+ if (de->ino == 0)
+ return -1;
+
+ fd = open(de->full_path, O_RDONLY);
+ if (fd < 0) {
+ MSG(0, "Skip: Fail to open %s\n", de->full_path);
+ return -1;
+ }
+
+ /* inline_data support */
+ if (de->size <= MAX_INLINE_DATA) {
+ struct node_info ni;
+ struct f2fs_node *node_blk;
+ int ret;
+
+ get_node_info(sbi, de->ino, &ni);
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ ret = dev_read_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ node_blk->i.i_inline |= F2FS_INLINE_DATA;
+ node_blk->i.i_inline |= F2FS_DATA_EXIST;
+ n = read(fd, buffer, BLOCK_SZ);
+ ASSERT(n == de->size);
+ memcpy(&node_blk->i.i_addr[1], buffer, de->size);
+
+ node_blk->i.i_size = cpu_to_le64(de->size);
+
+ ret = dev_write_block(node_blk, ni.blk_addr);
+ ASSERT(ret >= 0);
+ free(node_blk);
+ } else {
+ while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
+ f2fs_write_block(sbi, de->ino, buffer, n, off);
+ off += n;
+ }
+ }
+
+ close(fd);
+ if (n < 0)
+ return -1;
+
+ update_free_segments(sbi);
+
+ MSG(1, "Info: built a file %s, size=%lu\n", de->full_path, de->size);
+ return 0;
+}
diff --git a/fsck/sload.c b/fsck/sload.c
new file mode 100644
index 0000000..68799c1
--- /dev/null
+++ b/fsck/sload.c
@@ -0,0 +1,257 @@
+/**
+ * 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>
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#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->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);
+
+#ifdef HAVE_LIBSELINUX
+ 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);
+#endif
+
+ 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");
+ }
+
+#ifdef HAVE_LIBSELINUX
+ 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].secon);
+#endif
+
+ free(dentries[i].path);
+ free(dentries[i].full_path);
+ free((void *)dentries[i].name);
+ }
+
+ 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;
+ }
+
+#ifdef HAVE_LIBSELINUX
+ 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);
+ }
+#endif
+
+ /* 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
index 0000000..3f5c7d3
--- /dev/null
+++ b/fsck/xattr.c
@@ -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
index 0000000..b414629
--- /dev/null
+++ b/fsck/xattr.h
@@ -0,0 +1,65 @@
+/**
+ * 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.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
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 6ce58c2..97ee297 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -20,6 +20,10 @@
#include <config.h>
#endif
+#ifdef HAVE_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
+
typedef u_int64_t u64;
typedef u_int32_t u32;
typedef u_int16_t u16;
@@ -28,6 +32,7 @@
typedef u32 nid_t;
typedef u8 bool;
typedef unsigned long pgoff_t;
+typedef unsigned short umode_t;
#if HAVE_BYTESWAP_H
#include <byteswap.h>
@@ -124,7 +129,7 @@
do { \
printf("[ASSERT] (%s:%4d) ", __func__, __LINE__); \
printf(" --> "fmt"\n", ##__VA_ARGS__); \
- config.bug_on = 1; \
+ c.bug_on = 1; \
} while (0)
#define ASSERT(exp) \
@@ -143,14 +148,14 @@
#define MSG(n, fmt, ...) \
do { \
- if (config.dbg_lv >= n) { \
+ if (c.dbg_lv >= n) { \
printf(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define DBG(n, fmt, ...) \
do { \
- if (config.dbg_lv >= n) { \
+ if (c.dbg_lv >= n) { \
printf("[%s:%4d] " fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} \
@@ -165,15 +170,17 @@
#define DISP_u32(ptr, member) \
do { \
assert(sizeof((ptr)->member) <= 4); \
- printf("%-30s" "\t\t[0x%8x : %u]\n", \
- #member, ((ptr)->member), ((ptr)->member)); \
+ printf("%-30s" "\t\t[0x%8x : %u]\n", \
+ #member, le32_to_cpu(((ptr)->member)), \
+ le32_to_cpu(((ptr)->member))); \
} while (0)
#define DISP_u64(ptr, member) \
do { \
assert(sizeof((ptr)->member) == 8); \
printf("%-30s" "\t\t[0x%8llx : %llu]\n", \
- #member, ((ptr)->member), ((ptr)->member)); \
+ #member, le64_to_cpu(((ptr)->member)), \
+ le64_to_cpu(((ptr)->member))); \
} while (0)
#define DISP_utf(ptr, member) \
@@ -207,9 +214,13 @@
#define BITS_PER_BYTE 8
#define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */
#define CHECKSUM_OFFSET 4092
+#define MAX_PATH_LEN 64
+#define MAX_DEVICES 8
+
+#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
+#define F2FS_BLKSIZE_BITS 12
/* for mkfs */
-#define F2FS_MIN_VOLUME_SIZE 104857600
#define F2FS_NUMBER_OF_CHECKPOINT_PACK 2
#define DEFAULT_SECTOR_SIZE 512
#define DEFAULT_SECTORS_PER_BLOCK 8
@@ -221,18 +232,45 @@
enum f2fs_config_func {
FSCK,
DUMP,
+ DEFRAG,
+ RESIZE,
+ SLOAD,
+};
+
+struct device_info {
+ char *path;
+ int32_t fd;
+ u_int32_t sector_size;
+ u_int64_t total_sectors; /* got by get_device_info */
+ u_int64_t start_blkaddr;
+ u_int64_t end_blkaddr;
+ u_int32_t total_segments;
+
+ /* to handle zone block devices */
+ int zoned_model;
+ u_int32_t nr_zones;
+ u_int32_t nr_rnd_zones;
+ size_t zone_blocks;
};
struct f2fs_configuration {
- u_int32_t sector_size;
u_int32_t reserved_segments;
- u_int32_t overprovision;
+ u_int32_t new_reserved_segments;
+ int zoned_mode;
+ int zoned_model;
+ size_t zone_blocks;
+ double overprovision;
+ double new_overprovision;
u_int32_t cur_seg[6];
u_int32_t segs_per_sec;
u_int32_t secs_per_zone;
u_int32_t segs_per_zone;
u_int32_t start_sector;
+ u_int32_t total_segments;
+ u_int32_t sector_size;
u_int64_t total_sectors;
+ u_int64_t wanted_total_sectors;
+ u_int64_t target_sectors;
u_int32_t sectors_per_blk;
u_int32_t blks_per_seg;
__u8 init_version[VERSION_LEN + 1];
@@ -240,10 +278,12 @@
__u8 version[VERSION_LEN + 1];
char *vol_label;
int heap;
- int32_t fd, kd;
+ int32_t kd;
int32_t dump_fd;
- char *device_name;
+ struct device_info devices[MAX_DEVICES];
+ int ndevs;
char *extension_list;
+ const char *rootdev_name;
int dbg_lv;
int trim;
int func;
@@ -251,6 +291,19 @@
int fix_on;
int bug_on;
int auto_fix;
+ int preen_mode;
+ int ro;
+ __le32 feature; /* defined features */
+
+ /* defragmentation parameters */
+ int defrag_shrink;
+ 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
@@ -262,6 +315,92 @@
#define BIT_MASK(nr) (1 << (nr % BITS_PER_LONG))
#define BIT_WORD(nr) (nr / BITS_PER_LONG)
+#define set_sb_le64(member, val) (sb->member = cpu_to_le64(val))
+#define set_sb_le32(member, val) (sb->member = cpu_to_le32(val))
+#define set_sb_le16(member, val) (sb->member = cpu_to_le16(val))
+#define get_sb_le64(member) le64_to_cpu(sb->member)
+#define get_sb_le32(member) le32_to_cpu(sb->member)
+#define get_sb_le16(member) le16_to_cpu(sb->member)
+#define get_newsb_le64(member) le64_to_cpu(new_sb->member)
+#define get_newsb_le32(member) le32_to_cpu(new_sb->member)
+#define get_newsb_le16(member) le16_to_cpu(new_sb->member)
+
+#define set_sb(member, val) \
+ do { \
+ typeof(sb->member) t; \
+ switch (sizeof(t)) { \
+ case 8: set_sb_le64(member, val); break; \
+ case 4: set_sb_le32(member, val); break; \
+ case 2: set_sb_le16(member, val); break; \
+ } \
+ } while(0)
+
+#define get_sb(member) \
+ ({ \
+ typeof(sb->member) t; \
+ switch (sizeof(t)) { \
+ case 8: t = get_sb_le64(member); break; \
+ case 4: t = get_sb_le32(member); break; \
+ case 2: t = get_sb_le16(member); break; \
+ } \
+ t; \
+ })
+#define get_newsb(member) \
+ ({ \
+ typeof(new_sb->member) t; \
+ switch (sizeof(t)) { \
+ case 8: t = get_newsb_le64(member); break; \
+ case 4: t = get_newsb_le32(member); break; \
+ case 2: t = get_newsb_le16(member); break; \
+ } \
+ t; \
+ })
+
+#define set_cp_le64(member, val) (cp->member = cpu_to_le64(val))
+#define set_cp_le32(member, val) (cp->member = cpu_to_le32(val))
+#define set_cp_le16(member, val) (cp->member = cpu_to_le16(val))
+#define get_cp_le64(member) le64_to_cpu(cp->member)
+#define get_cp_le32(member) le32_to_cpu(cp->member)
+#define get_cp_le16(member) le16_to_cpu(cp->member)
+
+#define set_cp(member, val) \
+ do { \
+ typeof(cp->member) t; \
+ switch (sizeof(t)) { \
+ case 8: set_cp_le64(member, val); break; \
+ case 4: set_cp_le32(member, val); break; \
+ case 2: set_cp_le16(member, val); break; \
+ } \
+ } while(0)
+
+#define get_cp(member) \
+ ({ \
+ typeof(cp->member) t; \
+ switch (sizeof(t)) { \
+ case 8: t = get_cp_le64(member); break; \
+ case 4: t = get_cp_le32(member); break; \
+ case 2: t = get_cp_le16(member); break; \
+ } \
+ t; \
+ })
+
+/*
+ * Copied from include/linux/kernel.h
+ */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_down(x, y) ((x) & ~__round_mask(x, y))
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+
/*
* Copied from fs/f2fs/f2fs.h
*/
@@ -279,6 +418,8 @@
NO_CHECK_TYPE
};
+#define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */
+
/*
* Copied from fs/f2fs/segment.h
*/
@@ -315,9 +456,19 @@
#define MAX_ACTIVE_NODE_LOGS 8
#define MAX_ACTIVE_DATA_LOGS 8
+#define F2FS_FEATURE_ENCRYPT 0x0001
+#define F2FS_FEATURE_BLKZONED 0x0002
+
+#define MAX_VOLUME_NAME 512
+
/*
* For superblock
*/
+struct f2fs_device {
+ __u8 path[MAX_PATH_LEN];
+ __le32 total_segments;
+} __attribute__((packed));
+
struct f2fs_super_block {
__le32 magic; /* Magic Number */
__le16 major_ver; /* Major Version */
@@ -347,12 +498,17 @@
__le32 node_ino; /* node inode number */
__le32 meta_ino; /* meta inode number */
__u8 uuid[16]; /* 128-bit uuid for volume */
- __le16 volume_name[512]; /* volume name */
+ __le16 volume_name[MAX_VOLUME_NAME]; /* volume name */
__le32 extension_count; /* # of extensions below */
__u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
__le32 cp_payload;
__u8 version[VERSION_LEN]; /* the kernel version */
__u8 init_version[VERSION_LEN]; /* the initial kernel version */
+ __le32 feature; /* defined features */
+ __u8 encryption_level; /* versioning level for encryption */
+ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
+ struct f2fs_device devs[MAX_DEVICES]; /* device list */
+ __u8 reserved[327]; /* valid reserved region */
} __attribute__((packed));
/*
@@ -422,7 +578,9 @@
#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 */
@@ -436,15 +594,25 @@
#define F2FS_INLINE_DATA 0x02 /* file inline data flag */
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
#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))
#define DEF_DIR_LEVEL 0
+/*
+ * i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
+ */
+#define FADVISE_COLD_BIT 0x01
+#define FADVISE_LOST_PINO_BIT 0x02
+#define FADVISE_ENCRYPT_BIT 0x04
+
+#define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
+
struct f2fs_inode {
__le16 i_mode; /* file mode */
__u8 i_advise; /* file hints */
@@ -517,6 +685,7 @@
* 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 */
@@ -543,7 +712,9 @@
* disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments.
*/
#define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2)
-#define MAX_SIT_BITMAP_SIZE ((F2FS_MAX_SEGMENT / SIT_ENTRY_PER_BLOCK) / 8)
+#define MAX_SIT_BITMAP_SIZE (SEG_ALIGN(ALIGN(F2FS_MAX_SEGMENT, \
+ SIT_ENTRY_PER_BLOCK)) * \
+ c.blks_per_seg / 8)
/*
* Note that f2fs_sit_entry->vblocks has the following bit-field information.
@@ -606,7 +777,7 @@
struct summary_footer {
unsigned char entry_type; /* SUM_TYPE_XXX */
- __u32 check_sum; /* summary checksum */
+ __le32 check_sum; /* summary checksum */
} __attribute__((packed));
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
@@ -619,6 +790,13 @@
sizeof(struct sit_journal_entry))
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
sizeof(struct sit_journal_entry))
+
+/*
+ * Reserved area should make size of f2fs_extra_info equals to
+ * that of nat_journal and sit_journal.
+ */
+#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8)
+
/*
* frequently updated NAT/SIT entries can be stored in the spare area in
* summary blocks
@@ -648,18 +826,28 @@
__u8 reserved[SIT_JOURNAL_RESERVED];
} __attribute__((packed));
-/* 4KB-sized summary block structure */
-struct f2fs_summary_block {
- struct f2fs_summary entries[ENTRIES_IN_SUM];
+struct f2fs_extra_info {
+ __le64 kbytes_written;
+ __u8 reserved[EXTRA_INFO_RESERVED];
+} __attribute__((packed));
+
+struct f2fs_journal {
union {
__le16 n_nats;
__le16 n_sits;
};
- /* spare area is used by NAT or SIT journals */
+ /* spare area is used by NAT or SIT journals or extra info */
union {
struct nat_journal nat_j;
struct sit_journal sit_j;
+ struct f2fs_extra_info info;
};
+} __attribute__((packed));
+
+/* 4KB-sized summary block structure */
+struct f2fs_summary_block {
+ struct f2fs_summary entries[ENTRIES_IN_SUM];
+ struct f2fs_journal journal;
struct summary_footer footer;
} __attribute__((packed));
@@ -685,6 +873,9 @@
/* MAX level for dir lookup */
#define MAX_DIR_HASH_DEPTH 63
+/* MAX buckets in one level of dir */
+#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
+
#define SIZE_OF_DIR_ENTRY 11 /* by byte */
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
BITS_PER_BYTE)
@@ -725,7 +916,7 @@
__u8 reserved[INLINE_RESERVED_SIZE];
struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
__u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
-} __packed;
+} __attribute__((packed));
/* file types used in inode_info->flags */
enum FILE_TYPE {
@@ -750,27 +941,30 @@
SSR
};
-extern void ASCIIToUNICODE(u_int16_t *, u_int8_t *);
+extern int utf8_to_utf16(u_int16_t *, const char *, size_t, size_t);
+extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t);
extern int log_base_2(u_int32_t);
extern unsigned int addrs_per_inode(struct f2fs_inode *);
extern int get_bits_in_byte(unsigned char n);
-extern int set_bit(unsigned int nr,void * addr);
-extern int clear_bit(unsigned int nr, void * addr);
-extern int test_bit(unsigned int nr, const void * addr);
+extern int test_and_set_bit_le(u32, u8 *);
+extern int test_and_clear_bit_le(u32, u8 *);
+extern int test_bit_le(u32, const u8 *);
extern int f2fs_test_bit(unsigned int, const char *);
extern int f2fs_set_bit(unsigned int, char *);
extern int f2fs_clear_bit(unsigned int, char *);
-extern unsigned long find_next_bit(const unsigned long *,
- unsigned long, unsigned long);
+extern u64 find_next_bit_le(const u8 *, u64, u64);
+extern u64 find_next_zero_bit_le(const u8 *, u64, u64);
extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int);
extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len);
-extern void f2fs_init_configuration(struct f2fs_configuration *);
-extern int f2fs_dev_is_umounted(struct f2fs_configuration *);
-extern int f2fs_get_device_info(struct f2fs_configuration *);
-extern void f2fs_finalize_device(struct f2fs_configuration *);
+extern void f2fs_init_configuration(void);
+extern int f2fs_devs_are_umounted(void);
+extern int f2fs_dev_is_umounted(char *);
+extern int f2fs_get_device_info(void);
+extern int get_device_info(int);
+extern void f2fs_finalize_device(void);
extern int dev_read(void *, __u64, size_t);
extern int dev_write(void *, __u64, size_t);
@@ -778,20 +972,115 @@
extern int dev_write_dump(void *, __u64, size_t);
/* All bytes in the buffer must be 0 use dev_fill(). */
extern int dev_fill(void *, __u64, size_t);
+extern int dev_fill_block(void *, __u64);
extern int dev_read_block(void *, __u64);
-extern int dev_read_blocks(void *, __u64, __u32 );
extern int dev_reada_block(__u64);
extern int dev_read_version(void *, __u64, size_t);
extern void get_kernel_version(__u8 *);
f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int);
-extern struct f2fs_configuration config;
+#define F2FS_ZONED_NONE 0
+#define F2FS_ZONED_HA 1
+#define F2FS_ZONED_HM 2
+
+#ifdef HAVE_LINUX_BLKZONED_H
+
+#define blk_zone_type(z) (z)->type
+#define blk_zone_conv(z) ((z)->type == BLK_ZONE_TYPE_CONVENTIONAL)
+#define blk_zone_seq_req(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_REQ)
+#define blk_zone_seq_pref(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_PREF)
+#define blk_zone_seq(z) (blk_zone_seq_req(z) || blk_zone_seq_pref(z))
+
+static inline const char *
+blk_zone_type_str(struct blk_zone *blkz)
+{
+ switch (blk_zone_type(blkz)) {
+ case BLK_ZONE_TYPE_CONVENTIONAL:
+ return( "Conventional" );
+ case BLK_ZONE_TYPE_SEQWRITE_REQ:
+ return( "Sequential-write-required" );
+ case BLK_ZONE_TYPE_SEQWRITE_PREF:
+ return( "Sequential-write-preferred" );
+ }
+ return( "Unknown-type" );
+}
+
+#define blk_zone_cond(z) (z)->cond
+
+static inline const char *
+blk_zone_cond_str(struct blk_zone *blkz)
+{
+ switch (blk_zone_cond(blkz)) {
+ case BLK_ZONE_COND_NOT_WP:
+ return "Not-write-pointer";
+ case BLK_ZONE_COND_EMPTY:
+ return "Empty";
+ case BLK_ZONE_COND_IMP_OPEN:
+ return "Implicit-open";
+ case BLK_ZONE_COND_EXP_OPEN:
+ return "Explicit-open";
+ case BLK_ZONE_COND_CLOSED:
+ return "Closed";
+ case BLK_ZONE_COND_READONLY:
+ return "Read-only";
+ case BLK_ZONE_COND_FULL:
+ return "Full";
+ case BLK_ZONE_COND_OFFLINE:
+ return "Offline";
+ }
+ return "Unknown-cond";
+}
+
+#define blk_zone_empty(z) (blk_zone_cond(z) == BLK_ZONE_COND_EMPTY)
+
+#define blk_zone_sector(z) (z)->start
+#define blk_zone_length(z) (z)->len
+#define blk_zone_wp_sector(z) (z)->wp
+#define blk_zone_need_reset(z) (int)(z)->reset
+#define blk_zone_non_seq(z) (int)(z)->non_seq
+
+#endif
+
+extern void f2fs_get_zoned_model(int);
+extern int f2fs_get_zone_blocks(int);
+extern int f2fs_check_zones(int);
+extern int f2fs_reset_zones(int);
+
+extern struct f2fs_configuration c;
#define ALIGN(val, size) ((val) + (size) - 1) / (size)
-#define SEG_ALIGN(blks) ALIGN(blks, config.blks_per_seg)
-#define ZONE_ALIGN(blks) ALIGN(blks, config.blks_per_seg * \
- config.segs_per_zone)
+#define SEG_ALIGN(blks) ALIGN(blks, c.blks_per_seg)
+#define ZONE_ALIGN(blks) ALIGN(blks, c.blks_per_seg * \
+ c.segs_per_zone)
+
+static inline double get_best_overprovision(struct f2fs_super_block *sb)
+{
+ double reserved, ovp, candidate, end, diff, space;
+ double max_ovp = 0, max_space = 0;
+
+ if (get_sb(segment_count_main) < 256) {
+ candidate = 10;
+ end = 95;
+ diff = 5;
+ } else {
+ candidate = 0.01;
+ end = 10;
+ diff = 0.01;
+ }
+
+ for (; candidate <= end; candidate += diff) {
+ reserved = (2 * (100 / candidate + 1) + 6) *
+ get_sb(segs_per_sec);
+ ovp = (get_sb(segment_count_main) - reserved) * candidate / 100;
+ space = get_sb(segment_count_main) - reserved - ovp;
+ if (max_space < space) {
+ max_space = space;
+ max_ovp = candidate;
+ }
+ }
+ return max_ovp;
+}
#endif /*__F2FS_FS_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a6b304c..b26b404 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -2,6 +2,7 @@
lib_LTLIBRARIES = libf2fs.la
-libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c
+libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c
libf2fs_la_CFLAGS = -Wall
libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include
+libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE)
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 9e19e9b..93d3da9 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -19,22 +19,190 @@
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
+#ifndef WITH_ANDROID
+#include <scsi/sg.h>
+#endif
#include <linux/hdreg.h>
+#include <linux/limits.h>
#include <f2fs_fs.h>
-void ASCIIToUNICODE(u_int16_t *out_buf, u_int8_t *in_buf)
-{
- u_int8_t *pchTempPtr = in_buf;
- u_int16_t *pwTempPtr = out_buf;
+#ifndef WITH_ANDROID
+/* SCSI command for standard inquiry*/
+#define MODELINQUIRY 0x12,0x00,0x00,0x00,0x4A,0x00
+#endif
- while (*pchTempPtr != '\0') {
- *pwTempPtr = (u_int16_t)*pchTempPtr;
- pchTempPtr++;
- pwTempPtr++;
+/*
+ * UTF conversion codes are Copied from exfat tools.
+ */
+static const char *utf8_to_wchar(const char *input, wchar_t *wc,
+ size_t insize)
+{
+ if ((input[0] & 0x80) == 0 && insize >= 1) {
+ *wc = (wchar_t) input[0];
+ return input + 1;
}
- *pwTempPtr = '\0';
- return;
+ if ((input[0] & 0xe0) == 0xc0 && insize >= 2) {
+ *wc = (((wchar_t) input[0] & 0x1f) << 6) |
+ ((wchar_t) input[1] & 0x3f);
+ return input + 2;
+ }
+ if ((input[0] & 0xf0) == 0xe0 && insize >= 3) {
+ *wc = (((wchar_t) input[0] & 0x0f) << 12) |
+ (((wchar_t) input[1] & 0x3f) << 6) |
+ ((wchar_t) input[2] & 0x3f);
+ return input + 3;
+ }
+ if ((input[0] & 0xf8) == 0xf0 && insize >= 4) {
+ *wc = (((wchar_t) input[0] & 0x07) << 18) |
+ (((wchar_t) input[1] & 0x3f) << 12) |
+ (((wchar_t) input[2] & 0x3f) << 6) |
+ ((wchar_t) input[3] & 0x3f);
+ return input + 4;
+ }
+ if ((input[0] & 0xfc) == 0xf8 && insize >= 5) {
+ *wc = (((wchar_t) input[0] & 0x03) << 24) |
+ (((wchar_t) input[1] & 0x3f) << 18) |
+ (((wchar_t) input[2] & 0x3f) << 12) |
+ (((wchar_t) input[3] & 0x3f) << 6) |
+ ((wchar_t) input[4] & 0x3f);
+ return input + 5;
+ }
+ if ((input[0] & 0xfe) == 0xfc && insize >= 6) {
+ *wc = (((wchar_t) input[0] & 0x01) << 30) |
+ (((wchar_t) input[1] & 0x3f) << 24) |
+ (((wchar_t) input[2] & 0x3f) << 18) |
+ (((wchar_t) input[3] & 0x3f) << 12) |
+ (((wchar_t) input[4] & 0x3f) << 6) |
+ ((wchar_t) input[5] & 0x3f);
+ return input + 6;
+ }
+ return NULL;
+}
+
+static u_int16_t *wchar_to_utf16(u_int16_t *output, wchar_t wc, size_t outsize)
+{
+ if (wc <= 0xffff) {
+ if (outsize == 0)
+ return NULL;
+ output[0] = cpu_to_le16(wc);
+ return output + 1;
+ }
+ if (outsize < 2)
+ return NULL;
+ wc -= 0x10000;
+ output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
+ output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
+ return output + 2;
+}
+
+int utf8_to_utf16(u_int16_t *output, const char *input, size_t outsize,
+ size_t insize)
+{
+ const char *inp = input;
+ u_int16_t *outp = output;
+ wchar_t wc;
+
+ while ((size_t)(inp - input) < insize && *inp) {
+ inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
+ if (inp == NULL) {
+ DBG(0, "illegal UTF-8 sequence\n");
+ return -EILSEQ;
+ }
+ outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
+ if (outp == NULL) {
+ DBG(0, "name is too long\n");
+ return -ENAMETOOLONG;
+ }
+ }
+ *outp = cpu_to_le16(0);
+ return 0;
+}
+
+static const u_int16_t *utf16_to_wchar(const u_int16_t *input, wchar_t *wc,
+ size_t insize)
+{
+ if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800) {
+ if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
+ return NULL;
+ *wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
+ *wc |= (le16_to_cpu(input[1]) & 0x3ff);
+ *wc += 0x10000;
+ return input + 2;
+ } else {
+ *wc = le16_to_cpu(*input);
+ return input + 1;
+ }
+}
+
+static char *wchar_to_utf8(char *output, wchar_t wc, size_t outsize)
+{
+ if (wc <= 0x7f) {
+ if (outsize < 1)
+ return NULL;
+ *output++ = (char) wc;
+ } else if (wc <= 0x7ff) {
+ if (outsize < 2)
+ return NULL;
+ *output++ = 0xc0 | (wc >> 6);
+ *output++ = 0x80 | (wc & 0x3f);
+ } else if (wc <= 0xffff) {
+ if (outsize < 3)
+ return NULL;
+ *output++ = 0xe0 | (wc >> 12);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ } else if (wc <= 0x1fffff) {
+ if (outsize < 4)
+ return NULL;
+ *output++ = 0xf0 | (wc >> 18);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ } else if (wc <= 0x3ffffff) {
+ if (outsize < 5)
+ return NULL;
+ *output++ = 0xf8 | (wc >> 24);
+ *output++ = 0x80 | ((wc >> 18) & 0x3f);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ } else if (wc <= 0x7fffffff) {
+ if (outsize < 6)
+ return NULL;
+ *output++ = 0xfc | (wc >> 30);
+ *output++ = 0x80 | ((wc >> 24) & 0x3f);
+ *output++ = 0x80 | ((wc >> 18) & 0x3f);
+ *output++ = 0x80 | ((wc >> 12) & 0x3f);
+ *output++ = 0x80 | ((wc >> 6) & 0x3f);
+ *output++ = 0x80 | (wc & 0x3f);
+ } else
+ return NULL;
+
+ return output;
+}
+
+int utf16_to_utf8(char *output, const u_int16_t *input, size_t outsize,
+ size_t insize)
+{
+ const u_int16_t *inp = input;
+ char *outp = output;
+ wchar_t wc;
+
+ while ((size_t)(inp - input) < insize && le16_to_cpu(*inp)) {
+ inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
+ if (inp == NULL) {
+ DBG(0, "illegal UTF-16 sequence\n");
+ return -EILSEQ;
+ }
+ outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
+ if (outp == NULL) {
+ DBG(0, "name is too long\n");
+ return -ENAMETOOLONG;
+ }
+ }
+ *outp = '\0';
+ return 0;
}
int log_base_2(u_int32_t num)
@@ -75,37 +243,31 @@
return bits_in_byte[n];
}
-int set_bit(unsigned int nr,void * addr)
+int test_and_set_bit_le(u32 nr, u8 *addr)
{
- int mask, retval;
- unsigned char *ADDR = (unsigned char *) addr;
+ int mask, retval;
- ADDR += nr >> 3;
+ addr += nr >> 3;
mask = 1 << ((nr & 0x07));
- retval = mask & *ADDR;
- *ADDR |= mask;
+ retval = mask & *addr;
+ *addr |= mask;
return retval;
}
-int clear_bit(unsigned int nr, void * addr)
+int test_and_clear_bit_le(u32 nr, u8 *addr)
{
- int mask, retval;
- unsigned char *ADDR = (unsigned char *) addr;
+ int mask, retval;
- ADDR += nr >> 3;
+ addr += nr >> 3;
mask = 1 << ((nr & 0x07));
- retval = mask & *ADDR;
- *ADDR &= ~mask;
+ retval = mask & *addr;
+ *addr &= ~mask;
return retval;
}
-int test_bit(unsigned int nr, const void * addr)
+int test_bit_le(u32 nr, const u8 *addr)
{
- const __u32 *p = (const __u32 *)addr;
-
- nr = nr ^ 0;
-
- return ((1 << (nr & 31)) & (p[nr >> 5])) != 0;
+ return ((1 << (nr & 7)) & (addr[nr >> 3]));
}
int f2fs_test_bit(unsigned int nr, const char *p)
@@ -142,24 +304,10 @@
return ret;
}
-static inline unsigned long __ffs(unsigned long word)
+static inline u64 __ffs(u8 word)
{
int num = 0;
-#if BITS_PER_LONG == 64
- if ((word & 0xffffffff) == 0) {
- num += 32;
- word >>= 32;
- }
-#endif
- if ((word & 0xffff) == 0) {
- num += 16;
- word >>= 16;
- }
- if ((word & 0xff) == 0) {
- num += 8;
- word >>= 8;
- }
if ((word & 0xf) == 0) {
num += 4;
word >>= 4;
@@ -173,43 +321,42 @@
return num;
}
-unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
- unsigned long offset)
+/* Copied from linux/lib/find_bit.c */
+#define BITMAP_FIRST_BYTE_MASK(start) (0xff << ((start) & (BITS_PER_BYTE - 1)))
+
+static u64 _find_next_bit_le(const u8 *addr, u64 nbits, u64 start, char invert)
{
- const unsigned long *p = addr + BIT_WORD(offset);
- unsigned long result = offset & ~(BITS_PER_LONG-1);
- unsigned long tmp;
+ u8 tmp;
- if (offset >= size)
- return size;
- size -= result;
- offset %= BITS_PER_LONG;
- if (offset) {
- tmp = *(p++);
- tmp &= (~0UL << offset);
- if (size < BITS_PER_LONG)
- goto found_first;
- if (tmp)
- goto found_middle;
- size -= BITS_PER_LONG;
- result += BITS_PER_LONG;
- }
- while (size & ~(BITS_PER_LONG-1)) {
- if ((tmp = *(p++)))
- goto found_middle;
- result += BITS_PER_LONG;
- size -= BITS_PER_LONG;
- }
- if (!size)
- return result;
- tmp = *p;
+ if (!nbits || start >= nbits)
+ return nbits;
-found_first:
- tmp &= (~0UL >> (BITS_PER_LONG - size));
- if (tmp == 0UL) /* Are any bits set? */
- return result + size; /* Nope. */
-found_middle:
- return result + __ffs(tmp);
+ tmp = addr[start / BITS_PER_BYTE] ^ invert;
+
+ /* Handle 1st word. */
+ tmp &= BITMAP_FIRST_BYTE_MASK(start);
+ start = round_down(start, BITS_PER_BYTE);
+
+ while (!tmp) {
+ start += BITS_PER_BYTE;
+ if (start >= nbits)
+ return nbits;
+
+ tmp = addr[start / BITS_PER_BYTE] ^ invert;
+ }
+
+ return min(start + __ffs(tmp), nbits);
+}
+
+u64 find_next_bit_le(const u8 *addr, u64 size, u64 offset)
+{
+ return _find_next_bit_le(addr, size, offset, 0);
+}
+
+
+u64 find_next_zero_bit_le(const u8 *addr, u64 size, u64 offset)
+{
+ return _find_next_bit_le(addr, size, offset, 0xff);
}
/*
@@ -343,25 +490,89 @@
}
/*
+ * try to identify the root device
+ */
+const char *get_rootdev()
+{
+ struct stat sb;
+ int fd, ret;
+ char buf[32];
+ char *uevent, *ptr;
+
+ static char rootdev[PATH_MAX + 1];
+
+ if (stat("/", &sb) == -1)
+ return NULL;
+
+ snprintf(buf, 32, "/sys/dev/block/%u:%u/uevent",
+ major(sb.st_dev), minor(sb.st_dev));
+
+ fd = open(buf, O_RDONLY);
+
+ if (fd < 0)
+ return NULL;
+
+ ret = lseek(fd, (off_t)0, SEEK_END);
+ (void)lseek(fd, (off_t)0, SEEK_SET);
+
+ if (ret == -1) {
+ close(fd);
+ return NULL;
+ }
+
+ uevent = malloc(ret + 1);
+ uevent[ret] = '\0';
+
+ ret = read(fd, uevent, ret);
+ close(fd);
+
+ ptr = strstr(uevent, "DEVNAME");
+ if (!ptr)
+ return NULL;
+
+ ret = sscanf(ptr, "DEVNAME=%s\n", buf);
+ snprintf(rootdev, PATH_MAX + 1, "/dev/%s", buf);
+
+ return rootdev;
+}
+
+/*
* device information
*/
-void f2fs_init_configuration(struct f2fs_configuration *c)
+void f2fs_init_configuration(void)
{
- c->total_sectors = 0;
- c->sector_size = DEFAULT_SECTOR_SIZE;
- c->sectors_per_blk = DEFAULT_SECTORS_PER_BLOCK;
- c->blks_per_seg = DEFAULT_BLOCKS_PER_SEGMENT;
+ int i;
+
+ c.ndevs = 1;
+ c.total_sectors = 0;
+ c.sector_size = 0;
+ c.sectors_per_blk = DEFAULT_SECTORS_PER_BLOCK;
+ c.blks_per_seg = DEFAULT_BLOCKS_PER_SEGMENT;
+ c.rootdev_name = get_rootdev();
+ c.wanted_total_sectors = -1;
+ c.zoned_mode = 0;
+ c.zoned_model = 0;
+ c.zone_blocks = 0;
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ memset(&c.devices[i], 0, sizeof(struct device_info));
+ c.devices[i].fd = -1;
+ c.devices[i].sector_size = DEFAULT_SECTOR_SIZE;
+ c.devices[i].end_blkaddr = -1;
+ c.devices[i].zoned_model = F2FS_ZONED_NONE;
+ }
/* calculated by overprovision ratio */
- c->reserved_segments = 48;
- c->overprovision = 5;
- c->segs_per_sec = 1;
- c->secs_per_zone = 1;
- c->segs_per_zone = 1;
- c->heap = 1;
- c->vol_label = "";
- c->device_name = NULL;
- c->trim = 1;
+ c.reserved_segments = 0;
+ c.overprovision = 0;
+ c.segs_per_sec = 1;
+ c.secs_per_zone = 1;
+ c.segs_per_zone = 1;
+ c.heap = 1;
+ c.vol_label = "";
+ c.trim = 1;
+ c.ro = 0;
+ c.kd = -1;
}
static int is_mounted(const char *mpt, const char *device)
@@ -374,40 +585,62 @@
return 0;
while ((mnt = getmntent(file)) != NULL) {
- if (!strcmp(device, mnt->mnt_fsname))
+ if (!strcmp(device, mnt->mnt_fsname)) {
+#ifdef MNTOPT_RO
+ if (hasmntopt(mnt, MNTOPT_RO))
+ c.ro = 1;
+#endif
break;
+ }
}
endmntent(file);
return mnt ? 1 : 0;
}
-int f2fs_dev_is_umounted(struct f2fs_configuration *c)
+int f2fs_dev_is_umounted(char *path)
{
struct stat st_buf;
+ int is_rootdev = 0;
int ret = 0;
- ret = is_mounted(MOUNTED, c->device_name);
+ if (c.rootdev_name && !strcmp(path, c.rootdev_name))
+ is_rootdev = 1;
+
+ /*
+ * try with /proc/mounts fist to detect RDONLY.
+ * f2fs_stop_checkpoint makes RO in /proc/mounts while RW in /etc/mtab.
+ */
+ ret = is_mounted("/proc/mounts", path);
if (ret) {
- MSG(0, "\tError: Not available on mounted device!\n");
+ MSG(0, "Info: Mounted device!\n");
+ return -1;
+ }
+
+ ret = is_mounted(MOUNTED, path);
+ if (ret) {
+ MSG(0, "Info: Mounted device!\n");
return -1;
}
/*
- * if failed due to /etc/mtab file not present
- * try with /proc/mounts.
+ * If we are supposed to operate on the root device, then
+ * also check the mounts for '/dev/root', which sometimes
+ * functions as an alias for the root device.
*/
- ret = is_mounted("/proc/mounts", c->device_name);
- if (ret) {
- MSG(0, "\tError: Not available on mounted device!\n");
- return -1;
+ if (is_rootdev) {
+ ret = is_mounted("/proc/mounts", "/dev/root");
+ if (ret) {
+ MSG(0, "Info: Mounted device!\n");
+ return -1;
+ }
}
/*
* If f2fs is umounted with -l, the process can still use
* the file system. In this case, we should not format.
*/
- if (stat(c->device_name, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) {
- int fd = open(c->device_name, O_RDONLY | O_EXCL);
+ if (stat(path, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) {
+ int fd = open(path, O_RDONLY | O_EXCL);
if (fd >= 0) {
close(fd);
@@ -419,6 +652,16 @@
return 0;
}
+int f2fs_devs_are_umounted(void)
+{
+ int i;
+
+ for (i = 0; i < c.ndevs; i++)
+ if (f2fs_dev_is_umounted((char *)c.devices[i].path))
+ return -1;
+ return 0;
+}
+
void get_kernel_version(__u8 *version)
{
int i;
@@ -429,7 +672,7 @@
memset(version + i, 0, VERSION_LEN + 1 - i);
}
-int f2fs_get_device_info(struct f2fs_configuration *c)
+int get_device_info(int i)
{
int32_t fd = 0;
uint32_t sector_size;
@@ -438,18 +681,28 @@
#endif
struct stat stat_buf;
struct hd_geometry geom;
- u_int64_t wanted_total_sectors = c->total_sectors;
+#ifndef WITH_ANDROID
+ sg_io_hdr_t io_hdr;
+ unsigned char reply_buffer[96] = {0};
+ unsigned char model_inq[6] = {MODELINQUIRY};
+#endif
+ struct device_info *dev = c.devices + i;
- fd = open(c->device_name, O_RDWR);
+ fd = open((char *)dev->path, O_RDWR);
if (fd < 0) {
MSG(0, "\tError: Failed to open the device!\n");
return -1;
}
- c->fd = fd;
- c->kd = open("/proc/version", O_RDONLY);
- if (c->kd < 0)
- MSG(0, "\tInfo: No support kernel version!\n");
+ dev->fd = fd;
+
+ if (c.kd == -1) {
+ c.kd = open("/proc/version", O_RDONLY);
+ if (c.kd < 0) {
+ MSG(0, "\tInfo: No support kernel version!\n");
+ c.kd = -2;
+ }
+ }
if (fstat(fd, &stat_buf) < 0 ) {
MSG(0, "\tError: Failed to get the device stat!\n");
@@ -457,55 +710,144 @@
}
if (S_ISREG(stat_buf.st_mode)) {
- c->total_sectors = stat_buf.st_size / c->sector_size;
+ dev->total_sectors = stat_buf.st_size / dev->sector_size;
} else if (S_ISBLK(stat_buf.st_mode)) {
- if (ioctl(fd, BLKSSZGET, §or_size) < 0) {
+ if (ioctl(fd, BLKSSZGET, §or_size) < 0)
MSG(0, "\tError: Using the default sector size\n");
- } else {
- if (c->sector_size < sector_size) {
- c->sector_size = sector_size;
- c->sectors_per_blk = PAGE_SIZE / sector_size;
- }
- }
-
+ else if (dev->sector_size < sector_size)
+ dev->sector_size = sector_size;
#ifdef BLKGETSIZE64
- if (ioctl(fd, BLKGETSIZE64, &c->total_sectors) < 0) {
+ if (ioctl(fd, BLKGETSIZE64, &dev->total_sectors) < 0) {
MSG(0, "\tError: Cannot get the device size\n");
return -1;
}
- c->total_sectors /= c->sector_size;
#else
if (ioctl(fd, BLKGETSIZE, &total_sectors) < 0) {
MSG(0, "\tError: Cannot get the device size\n");
return -1;
}
- total_sectors /= c->sector_size;
- c->total_sectors = total_sectors;
+ dev->total_sectors = total_sectors;
#endif
+ dev->total_sectors /= dev->sector_size;
+
if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
- c->start_sector = 0;
+ c.start_sector = 0;
else
- c->start_sector = geom.start;
+ c.start_sector = geom.start;
+
+#ifndef WITH_ANDROID
+ /* Send INQUIRY command */
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = sizeof(reply_buffer);
+ io_hdr.dxferp = reply_buffer;
+ io_hdr.cmd_len = sizeof(model_inq);
+ io_hdr.cmdp = model_inq;
+ io_hdr.timeout = 1000;
+
+ if (!ioctl(fd, SG_IO, &io_hdr)) {
+ int i = 16;
+
+ MSG(0, "Info: [%s] Disk Model: ",
+ dev->path);
+ while (reply_buffer[i] != '`' && i < 80)
+ printf("%c", reply_buffer[i++]);
+ printf("\n");
+ }
+#endif
} else {
MSG(0, "\tError: Volume type is not supported!!!\n");
return -1;
}
- if (wanted_total_sectors && wanted_total_sectors < c->total_sectors) {
- MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n",
- c->total_sectors, c->sector_size);
- c->total_sectors = wanted_total_sectors;
- }
- MSG(0, "Info: sector size = %u\n", c->sector_size);
- MSG(0, "Info: total sectors = %"PRIu64" (in %u bytes)\n",
- c->total_sectors, c->sector_size);
- if (c->total_sectors <
- (F2FS_MIN_VOLUME_SIZE / c->sector_size)) {
- MSG(0, "Error: Min volume size supported is %d\n",
- F2FS_MIN_VOLUME_SIZE);
+ if (!c.sector_size) {
+ c.sector_size = dev->sector_size;
+ c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size;
+ } else if (c.sector_size != c.devices[i].sector_size) {
+ MSG(0, "\tError: Different sector sizes!!!\n");
return -1;
}
+#ifndef WITH_ANDROID
+ if (S_ISBLK(stat_buf.st_mode))
+ f2fs_get_zoned_model(i);
+
+ if (dev->zoned_model != F2FS_ZONED_NONE) {
+ if (dev->zoned_model == F2FS_ZONED_HM)
+ c.zoned_model = F2FS_ZONED_HM;
+
+ if (f2fs_get_zone_blocks(i)) {
+ MSG(0, "\tError: Failed to get number of blocks per zone\n");
+ return -1;
+ }
+
+ if (f2fs_check_zones(i)) {
+ MSG(0, "\tError: Failed to check zone configuration\n");
+ return -1;
+ }
+ MSG(0, "Info: Host-%s zoned block device:\n",
+ (dev->zoned_model == F2FS_ZONED_HA) ?
+ "aware" : "managed");
+ MSG(0, " %u zones, %u randomly writeable zones\n",
+ dev->nr_zones, dev->nr_rnd_zones);
+ MSG(0, " %lu blocks per zone\n",
+ dev->zone_blocks);
+ }
+#endif
+ c.total_sectors += dev->total_sectors;
return 0;
}
+int f2fs_get_device_info(void)
+{
+ int i;
+
+ for (i = 0; i < c.ndevs; i++)
+ if (get_device_info(i))
+ return -1;
+
+ if (c.wanted_total_sectors < c.total_sectors) {
+ MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n",
+ c.total_sectors, c.sector_size);
+ c.total_sectors = c.wanted_total_sectors;
+ c.devices[0].total_sectors = c.total_sectors;
+ }
+ if (c.total_sectors * c.sector_size >
+ (u_int64_t)F2FS_MAX_SEGMENT * 2 * 1024 * 1024) {
+ MSG(0, "\tError: F2FS can support 16TB at most!!!\n");
+ return -1;
+ }
+
+ for (i = 0; i < c.ndevs; i++) {
+ if (c.devices[i].zoned_model != F2FS_ZONED_NONE) {
+ if (c.zone_blocks &&
+ c.zone_blocks != c.devices[i].zone_blocks) {
+ MSG(0, "\tError: not support different zone sizes!!!\n");
+ return -1;
+ }
+ c.zone_blocks = c.devices[i].zone_blocks;
+ }
+ }
+
+ /*
+ * Align sections to the device zone size
+ * and align F2FS zones to the device zones.
+ */
+ if (c.zone_blocks) {
+ c.segs_per_sec = c.zone_blocks / DEFAULT_BLOCKS_PER_SEGMENT;
+ c.secs_per_zone = 1;
+ } else {
+ c.zoned_mode = 0;
+ }
+
+ c.segs_per_zone = c.segs_per_sec * c.secs_per_zone;
+
+ MSG(0, "Info: Segments per section = %d\n", c.segs_per_sec);
+ MSG(0, "Info: Sections per zone = %d\n", c.secs_per_zone);
+ MSG(0, "Info: sector size = %u\n", c.sector_size);
+ MSG(0, "Info: total sectors = %"PRIu64" (%"PRIu64" MB)\n",
+ c.total_sectors, (c.total_sectors *
+ (c.sector_size >> 9)) >> 11);
+ return 0;
+}
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index afa345f..c09db36 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -23,33 +23,58 @@
#include <f2fs_fs.h>
-struct f2fs_configuration config;
+struct f2fs_configuration c;
+
+static int __get_device_fd(__u64 *offset)
+{
+ __u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
+ int i;
+
+ for (i = 0; i < c.ndevs; i++) {
+ if (c.devices[i].start_blkaddr <= blk_addr &&
+ c.devices[i].end_blkaddr >= blk_addr) {
+ *offset -=
+ c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS;
+ return c.devices[i].fd;
+ }
+ }
+ return -1;
+}
/*
* IO interfaces
*/
int dev_read_version(void *buf, __u64 offset, size_t len)
{
- if (lseek64(config.kd, (off64_t)offset, SEEK_SET) < 0)
+ if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
return -1;
- if (read(config.kd, buf, len) < 0)
+ if (read(c.kd, buf, len) < 0)
return -1;
return 0;
}
int dev_read(void *buf, __u64 offset, size_t len)
{
- if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+ int fd = __get_device_fd(&offset);
+
+ if (fd < 0)
+ return fd;
+
+ if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
return -1;
- if (read(config.fd, buf, len) < 0)
+ if (read(fd, buf, len) < 0)
return -1;
return 0;
}
int dev_readahead(__u64 offset, size_t len)
{
+ int fd = __get_device_fd(&offset);
+
+ if (fd < 0)
+ return fd;
#ifdef POSIX_FADV_WILLNEED
- return posix_fadvise(config.fd, offset, len, POSIX_FADV_WILLNEED);
+ return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
#else
return 0;
#endif
@@ -57,65 +82,78 @@
int dev_write(void *buf, __u64 offset, size_t len)
{
- if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+ int fd = __get_device_fd(&offset);
+
+ if (fd < 0)
+ return fd;
+
+ if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
return -1;
- if (write(config.fd, buf, len) < 0)
+ if (write(fd, buf, len) < 0)
return -1;
return 0;
}
int dev_write_block(void *buf, __u64 blk_addr)
{
- return dev_write(buf, blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+ return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
}
int dev_write_dump(void *buf, __u64 offset, size_t len)
{
- if (lseek64(config.dump_fd, (off64_t)offset, SEEK_SET) < 0)
+ if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0)
return -1;
- if (write(config.dump_fd, buf, len) < 0)
+ if (write(c.dump_fd, buf, len) < 0)
return -1;
return 0;
}
int dev_fill(void *buf, __u64 offset, size_t len)
{
+ int fd = __get_device_fd(&offset);
+
+ if (fd < 0)
+ return fd;
+
/* Only allow fill to zero */
if (*((__u8*)buf))
return -1;
- if (lseek64(config.fd, (off64_t)offset, SEEK_SET) < 0)
+ if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
return -1;
- if (write(config.fd, buf, len) < 0)
+ if (write(fd, buf, len) < 0)
return -1;
return 0;
}
-int dev_read_block(void *buf, __u64 blk_addr)
+int dev_fill_block(void *buf, __u64 blk_addr)
{
- return dev_read(buf, blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+ return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
}
-int dev_read_blocks(void *buf, __u64 addr, __u32 nr_blks)
+int dev_read_block(void *buf, __u64 blk_addr)
{
- return dev_read(buf, addr * F2FS_BLKSIZE, nr_blks * F2FS_BLKSIZE);
+ return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
}
int dev_reada_block(__u64 blk_addr)
{
- return dev_readahead(blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE);
+ return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
}
-void f2fs_finalize_device(struct f2fs_configuration *c)
+void f2fs_finalize_device(void)
{
+ int i;
+
/*
* We should call fsync() to flush out all the dirty pages
* in the block device page cache.
*/
- if (fsync(c->fd) < 0)
- MSG(0, "\tError: Could not conduct fsync!!!\n");
+ for (i = 0; i < c.ndevs; i++) {
+ if (fsync(c.devices[i].fd) < 0)
+ MSG(0, "\tError: Could not conduct fsync!!!\n");
- if (close(c->fd) < 0)
- MSG(0, "\tError: Failed to close device file!!!\n");
-
- close(c->kd);
+ if (close(c.devices[i].fd) < 0)
+ MSG(0, "\tError: Failed to close device file!!!\n");
+ }
+ close(c.kd);
}
diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c
new file mode 100644
index 0000000..eebf030
--- /dev/null
+++ b/lib/libf2fs_zoned.c
@@ -0,0 +1,310 @@
+/**
+ * libf2fs_zoned.c
+ *
+ * Copyright (c) 2016 Western Digital Corporation.
+ * Written by: Damien Le Moal <damien.lemoal@wdc.com>
+ *
+ * Dual licensed under the GPL or LGPL version 2 licenses.
+ */
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <libgen.h>
+
+#include <f2fs_fs.h>
+
+#ifdef HAVE_LINUX_BLKZONED_H
+
+void f2fs_get_zoned_model(int i)
+{
+ struct device_info *dev = c.devices + i;
+ char str[128];
+ FILE *file;
+ int res;
+
+ /* Check that this is a zoned block device */
+ snprintf(str, sizeof(str),
+ "/sys/block/%s/queue/zoned",
+ basename(dev->path));
+ file = fopen(str, "r");
+ if (!file)
+ goto not_zoned;
+
+ memset(str, 0, sizeof(str));
+ res = fscanf(file, "%s", str);
+ fclose(file);
+
+ if (res != 1)
+ goto not_zoned;
+
+ if (strcmp(str, "host-aware") == 0) {
+ dev->zoned_model = F2FS_ZONED_HA;
+ return;
+ }
+ if (strcmp(str, "host-managed") == 0) {
+ dev->zoned_model = F2FS_ZONED_HM;
+ return;
+ }
+
+not_zoned:
+ dev->zoned_model = F2FS_ZONED_NONE;
+}
+
+int f2fs_get_zone_blocks(int i)
+{
+ struct device_info *dev = c.devices + i;
+ uint64_t sectors;
+ char str[128];
+ FILE *file;
+ int res;
+
+ /* Get zone size */
+ dev->zone_blocks = 0;
+
+ snprintf(str, sizeof(str),
+ "/sys/block/%s/queue/chunk_sectors",
+ basename(dev->path));
+ file = fopen(str, "r");
+ if (!file)
+ return -1;
+
+ memset(str, 0, sizeof(str));
+ res = fscanf(file, "%s", str);
+ fclose(file);
+
+ if (res != 1)
+ return -1;
+
+ sectors = atol(str);
+ if (!sectors)
+ return -1;
+
+ dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9);
+ sectors = (sectors << 9) / c.sector_size;
+
+ /*
+ * Total number of zones: there may
+ * be a last smaller runt zone.
+ */
+ dev->nr_zones = dev->total_sectors / sectors;
+ if (dev->total_sectors % sectors)
+ dev->nr_zones++;
+
+ return 0;
+}
+
+#define F2FS_REPORT_ZONES_BUFSZ 524288
+
+int f2fs_check_zones(int j)
+{
+ struct device_info *dev = c.devices + j;
+ struct blk_zone_report *rep;
+ struct blk_zone *blkz;
+ unsigned int i, n = 0;
+ u_int64_t total_sectors;
+ u_int64_t sector;
+ int last_is_conv = 1;
+ int ret = -1;
+
+ rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
+ if (!rep) {
+ ERR_MSG("No memory for report zones\n");
+ return -ENOMEM;
+ }
+
+ dev->nr_rnd_zones = 0;
+ sector = 0;
+ total_sectors = (dev->total_sectors * c.sector_size) >> 9;
+
+ while (sector < total_sectors) {
+
+ /* Get zone info */
+ memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
+ rep->sector = sector;
+ rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
+ / sizeof(struct blk_zone);
+
+ ret = ioctl(dev->fd, BLKREPORTZONE, rep);
+ if (ret != 0) {
+ ret = -errno;
+ ERR_MSG("ioctl BLKREPORTZONE failed\n");
+ goto out;
+ }
+
+ if (!rep->nr_zones)
+ break;
+
+ blkz = (struct blk_zone *)(rep + 1);
+ for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
+
+ if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY ||
+ blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE)
+ last_is_conv = 0;
+ if (blk_zone_conv(blkz) ||
+ blk_zone_seq_pref(blkz)) {
+ if (last_is_conv)
+ dev->nr_rnd_zones++;
+ } else {
+ last_is_conv = 0;
+ }
+
+ if (blk_zone_conv(blkz)) {
+ DBG(2,
+ "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n",
+ n,
+ blk_zone_cond(blkz),
+ blk_zone_cond_str(blkz),
+ blk_zone_sector(blkz),
+ blk_zone_length(blkz));
+ } else {
+ DBG(2,
+ "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, "
+ "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n",
+ n,
+ blk_zone_type(blkz),
+ blk_zone_type_str(blkz),
+ blk_zone_cond(blkz),
+ blk_zone_cond_str(blkz),
+ blk_zone_need_reset(blkz),
+ blk_zone_non_seq(blkz),
+ blk_zone_sector(blkz),
+ blk_zone_length(blkz),
+ blk_zone_wp_sector(blkz));
+ }
+
+ sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
+ n++;
+ blkz++;
+ }
+ }
+
+ if (sector != total_sectors) {
+ ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n",
+ (unsigned long long)(sector << 9) / c.sector_size,
+ (unsigned long long)dev->total_sectors);
+ ret = -1;
+ goto out;
+ }
+
+ if (n != dev->nr_zones) {
+ ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n",
+ dev->nr_zones, n);
+ ret = -1;
+ goto out;
+ }
+
+ if (dev->zoned_model == F2FS_ZONED_HM &&
+ !dev->nr_rnd_zones) {
+ ERR_MSG("No conventional zone for super block\n");
+ ret = -1;
+ }
+out:
+ free(rep);
+ return ret;
+}
+
+int f2fs_reset_zones(int j)
+{
+ struct device_info *dev = c.devices + j;
+ struct blk_zone_report *rep;
+ struct blk_zone *blkz;
+ struct blk_zone_range range;
+ u_int64_t total_sectors;
+ u_int64_t sector;
+ unsigned int i;
+ int ret = -1;
+
+ rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
+ if (!rep) {
+ ERR_MSG("No memory for report zones\n");
+ return -1;
+ }
+
+ sector = 0;
+ total_sectors = (dev->total_sectors * c.sector_size) >> 9;
+ while (sector < total_sectors) {
+
+ /* Get zone info */
+ memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
+ rep->sector = sector;
+ rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
+ / sizeof(struct blk_zone);
+
+ ret = ioctl(dev->fd, BLKREPORTZONE, rep);
+ if (ret != 0) {
+ ret = -errno;
+ ERR_MSG("ioctl BLKREPORTZONES failed\n");
+ goto out;
+ }
+
+ if (!rep->nr_zones)
+ break;
+
+ blkz = (struct blk_zone *)(rep + 1);
+ for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
+ if (blk_zone_seq(blkz) &&
+ !blk_zone_empty(blkz)) {
+ /* Non empty sequential zone: reset */
+ range.sector = blk_zone_sector(blkz);
+ range.nr_sectors = blk_zone_length(blkz);
+ ret = ioctl(dev->fd, BLKRESETZONE, &range);
+ if (ret != 0) {
+ ret = -errno;
+ ERR_MSG("ioctl BLKRESETZONE failed\n");
+ goto out;
+ }
+ }
+ sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
+ blkz++;
+ }
+ }
+out:
+ free(rep);
+ if (!ret)
+ MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20);
+ return ret;
+}
+
+#else
+
+void f2fs_get_zoned_model(int i)
+{
+ struct device_info *dev = c.devices + i;
+
+ c.zoned_mode = 0;
+ dev->zoned_model = F2FS_ZONED_NONE;
+}
+
+int f2fs_get_zone_blocks(int i)
+{
+ struct device_info *dev = c.devices + i;
+
+ c.zoned_mode = 0;
+ dev->nr_zones = 0;
+ dev->zone_blocks = 0;
+ dev->zoned_model = F2FS_ZONED_NONE;
+
+ return 0;
+}
+
+int f2fs_check_zones(int i)
+{
+ ERR_MSG("%d: Zoned block devices are not supported\n", i);
+ return -1;
+}
+
+int f2fs_reset_zones(int i)
+{
+ ERR_MSG("%d: Zoned block devices are not supported\n", i);
+ return -1;
+}
+
+#endif
+
diff --git a/man/Makefile.am b/man/Makefile.am
index 477d654..7856586 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
## Makefile.am
-dist_man_MANS = mkfs.f2fs.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8
diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
new file mode 100644
index 0000000..b08399b
--- /dev/null
+++ b/man/defrag.f2fs.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+.\"
+.TH DEFRAG.F2FS 8
+.SH NAME
+defrag.f2fs \- relocate blocks in a given area to the specified region
+.SH SYNOPSIS
+.B defrag.f2fs
+[
+.B \-s
+.I start block address
+]
+[
+.B \-l
+.I number of blocks
+]
+[
+.B \-t
+.I target block address
+]
+[
+.B \-i
+.I direction
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B defrag.f2fs
+is used to move specified number of blocks starting from a given block address
+to the target block address with a direction.
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+For example,
+# defrag.f2fs -s 0x4000 -l 0x100 -t 0x10000 -i /dev/sdb1
+
+This moves blocks between 0x4000 and 0x4100 to the left-hand area of 0x10000.
+
+.PP
+The exit code returned by
+.B defrag.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-s " start block address"
+Specify the starting block address.
+.TP
+.BI \-l " number of blocks"
+Specifiy the number of blocks to move.
+.TP
+.BI \-t " target block address"
+Specify the destination block address.
+.TP
+.BI \-i " direction"
+Set the direction to left. If it is not set, the direction becomes right
+by default.
+.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 defrag.f2fs
+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
+.SH AVAILABILITY
+.B defrag.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 dump.f2fs(8),
+.BR fsck.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
new file mode 100644
index 0000000..35616e5
--- /dev/null
+++ b/man/dump.f2fs.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) 2013 Samsung Electronics Co., Ltd.
+.\"
+.TH DUMP.F2FS 8
+.SH NAME
+dump.f2fs \- retrieve directory and file entries from an F2FS-formated image
+.SH SYNOPSIS
+.B dump.f2fs
+[
+.B \-i
+.I inode number
+]
+[
+.B \-s
+.I SIT range
+]
+[
+.B \-a
+.I SSA range
+]
+[
+.B \-b
+.I block address
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B dump.f2fs
+is used to retrieve f2fs metadata (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Currently, it can retrieve 1) a file given its inode number, 2) SIT entries into
+a file, 3) SSA entries into a file, 4) reverse information from the given block
+address.
+.PP
+The exit code returned by
+.B dump.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-i " inode number"
+Specify an inode number to dump out.
+.TP
+.BI \-s " SIT range"
+Specify a range presented by segment numbers to dump SIT entries.
+.TP
+.BI \-a " SSA range"
+Specify a range presented by segment numbers to dump SSA entries.
+.TP
+.BI \-b " block address"
+Specify a block address to retrieve its metadata information.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+Initial checking code was written by Byoung Geun Kim <bgbg.kim@samsung.com>.
+.SH AVAILABILITY
+.B dump.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 defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
new file mode 100644
index 0000000..af1076c
--- /dev/null
+++ b/man/fsck.f2fs.8
@@ -0,0 +1,69 @@
+.\" Copyright (c) 2013 Samsung Electronics Co., Ltd.
+.\"
+.TH FSCK.F2FS 8
+.SH NAME
+fsck.f2fs \- check a Linux F2FS file system
+.SH SYNOPSIS
+.B fsck.f2fs
+[
+.B \-a
+.I enable auto fix
+]
+[
+.B \-f
+.I enable force fix
+]
+[
+.B \-p
+.I enable preen mode
+]
+[
+.B \-t
+.I show stored directory tree
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B fsck.f2fs
+is used to check an f2fs file system (usually in 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 fsck.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-a " enable auto fix"
+Enable to run file system check only if a bug was reported by the F2FS kernel
+module. It is disabled by default.
+.TP
+.BI \-f " enable force fix"
+Enable to fix all the inconsistency in the partition.
+.TP
+.BI \-p " enable preen mode"
+Same as "-a" to support general fsck convention.
+.TP
+.BI \-t " show stored directory tree"
+Enable to show every directory entries in the partition.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+Initial checking code was written by Byoung Geun Kim <bgbg.kim@samsung.com>.
+Jaegeuk Kim <jaegeuk@kernel.org> reworked most parts of the codes to support
+fixing any corrupted images.
+.SH AVAILABILITY
+.B fsck.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 dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index f386ac6..bdb9755 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -2,7 +2,7 @@
.\" http://www.samsung.com/
.\" Written by Jaegeuk Kim <jaegeuk.kim@samsung.com>
.\"
-.TH MKFS.F2FS 8 "January 2013" "f2fs-tools version 1.2.0"
+.TH MKFS.F2FS 8
.SH NAME
mkfs.f2fs \- create an F2FS file system
.SH SYNOPSIS
@@ -12,6 +12,10 @@
.I heap-based-allocation
]
[
+.B \-c
+.I device
+]
+[
.B \-l
.I volume-label
]
@@ -21,7 +25,7 @@
]
[
.B \-s
-.I log-based-#-of-segments-per-section
+.I #-of-segments-per-section
]
[
.B \-z
@@ -36,11 +40,13 @@
.I debugging-level
]
.I device
+.I [sectors]
.SH DESCRIPTION
.B mkfs.f2fs
is used to create a f2fs file system (usually in a disk partition).
\fIdevice\fP is the special file corresponding to the device (e.g.
\fI/dev/sdXX\fP).
+\fIsectors\fP is optionally given for specifing the filesystem size.
.PP
The exit code returned by
.B mkfs.f2fs
@@ -53,17 +59,22 @@
assigned separately according to the whole volume size.
The default value is 1.
.TP
+.BI \-c " device"
+Build f2fs with this device additionally, so that user can see all
+the devices as one big volume.
+.TP
.BI \-l " volume-label"
Specify the volume label to the partition mounted as F2FS.
.TP
.BI \-o " overprovision-ratio-percentage"
Specify the percentage over the volume size for overprovision area. This area
-is hidden to users, and utilized by F2FS cleaner. The default percentage is 5%.
+is hidden to users, and utilized by F2FS cleaner. If not specified, the best
+number will be assigned automatically accoring to the partition size.
.TP
-.BI \-s " log-based-#-of-segments-per-section"
-Specify the log-based number of segments per section. A section consists of
+.BI \-s " #-of-segments-per-section"
+Specify the number of segments per section. A section consists of
multiple consecutive segments, and is the unit of garbage collection.
-The default number is 0, which means one segment is assigned to a section.
+The default number is 1, which means one segment is assigned to a section.
.TP
.BI \-z " #-of-sections-per-zone"
Specify the number of sections per zone. A zone consists of multiple sections.
@@ -88,4 +99,9 @@
.B mkfs.f2fs
is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
.SH SEE ALSO
-.BR mkfs (8).
+.BR mkfs (8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
new file mode 100644
index 0000000..463eca5
--- /dev/null
+++ b/man/resize.f2fs.8
@@ -0,0 +1,50 @@
+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+.\"
+.TH RESIZE.F2FS 8
+.SH NAME
+resize.f2fs \- resize filesystem size
+.SH SYNOPSIS
+.B resize.f2fs
+[
+.B \-t
+.I target sectors
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B resize.f2fs
+is used to resize an f2fs file system (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Current version only supports expanding the prebuilt filesystem.
+
+.PP
+The exit code returned by
+.B resize.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-t " target sectors"
+Specify the size in sectors.
+.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 resize.f2fs
+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
+.SH AVAILABILITY
+.B resize.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 sload.f2fs(8).
diff --git a/man/sload.f2fs.8 b/man/sload.f2fs.8
new file mode 100644
index 0000000..d07330c
--- /dev/null
+++ b/man/sload.f2fs.8
@@ -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).
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index fa48699..8b4c16c 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -3,5 +3,12 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall -DWITH_BLKDISCARD
sbin_PROGRAMS = mkfs.f2fs
-mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c
+mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c f2fs_format_utils.h $(top_srcdir)/include/f2fs_fs.h
mkfs_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+
+lib_LTLIBRARIES = libf2fs_format.la
+libf2fs_format_la_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c
+libf2fs_format_la_CFLAGS = -DWITH_BLKDISCARD
+libf2fs_format_la_CPPFLAGS = -I$(top_srcdir)/include
+libf2fs_format_la_LDFLAGS = -luuid -L$(top_srcdir)/lib -lf2fs \
+ -version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE)
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 094afa3..3c13026 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -21,72 +21,16 @@
#include "f2fs_fs.h"
#include "f2fs_format_utils.h"
-extern struct f2fs_configuration config;
-struct f2fs_super_block sb;
+extern struct f2fs_configuration c;
+struct f2fs_super_block raw_sb;
+struct f2fs_super_block *sb = &raw_sb;
struct f2fs_checkpoint *cp;
/* Return first segment number of each area */
-#define prev_zone(cur) (config.cur_seg[cur] - config.segs_per_zone)
-#define next_zone(cur) (config.cur_seg[cur] + config.segs_per_zone)
-#define last_zone(cur) ((cur - 1) * config.segs_per_zone)
-#define last_section(cur) (cur + (config.secs_per_zone - 1) * config.segs_per_sec)
-
-#define set_sb_le64(member, val) (sb.member = cpu_to_le64(val))
-#define set_sb_le32(member, val) (sb.member = cpu_to_le32(val))
-#define set_sb_le16(member, val) (sb.member = cpu_to_le16(val))
-#define get_sb_le64(member) le64_to_cpu(sb.member)
-#define get_sb_le32(member) le32_to_cpu(sb.member)
-#define get_sb_le16(member) le16_to_cpu(sb.member)
-
-#define set_sb(member, val) \
- do { \
- typeof(sb.member) t; \
- switch (sizeof(t)) { \
- case 8: set_sb_le64(member, val); break; \
- case 4: set_sb_le32(member, val); break; \
- case 2: set_sb_le16(member, val); break; \
- } \
- } while(0)
-
-#define get_sb(member) \
- ({ \
- typeof(sb.member) t; \
- switch (sizeof(t)) { \
- case 8: t = get_sb_le64(member); break; \
- case 4: t = get_sb_le32(member); break; \
- case 2: t = get_sb_le16(member); break; \
- } \
- t; \
- })
-
-#define set_cp_le64(member, val) (cp->member = cpu_to_le64(val))
-#define set_cp_le32(member, val) (cp->member = cpu_to_le32(val))
-#define set_cp_le16(member, val) (cp->member = cpu_to_le16(val))
-#define get_cp_le64(member) le64_to_cpu(cp->member)
-#define get_cp_le32(member) le32_to_cpu(cp->member)
-#define get_cp_le16(member) le16_to_cpu(cp->member)
-
-#define set_cp(member, val) \
- do { \
- typeof(cp->member) t; \
- switch (sizeof(t)) { \
- case 8: set_cp_le64(member, val); break; \
- case 4: set_cp_le32(member, val); break; \
- case 2: set_cp_le16(member, val); break; \
- } \
- } while(0)
-
-#define get_cp(member) \
- ({ \
- typeof(cp->member) t; \
- switch (sizeof(t)) { \
- case 8: t = get_cp_le64(member); break; \
- case 4: t = get_cp_le32(member); break; \
- case 2: t = get_cp_le16(member); break; \
- } \
- t; \
- })
-
+#define prev_zone(cur) (c.cur_seg[cur] - c.segs_per_zone)
+#define next_zone(cur) (c.cur_seg[cur] + c.segs_per_zone)
+#define last_zone(cur) ((cur - 1) * c.segs_per_zone)
+#define last_section(cur) (cur + (c.secs_per_zone - 1) * c.segs_per_sec)
const char *media_ext_lists[] = {
"jpg",
@@ -118,21 +62,33 @@
NULL
};
-static void configure_extension_list(void)
+static bool is_extension_exist(const char *name)
+{
+ int i;
+
+ for (i = 0; i < F2FS_MAX_EXTENSION; i++) {
+ char *ext = (char *)sb->extension_list[i];
+ if (!strcmp(ext, name))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void cure_extension_list(void)
{
const char **extlist = media_ext_lists;
- char *ext_str = config.extension_list;
+ char *ext_str = c.extension_list;
char *ue;
int name_len;
int i = 0;
- sb.extension_count = 0;
- memset(sb.extension_list, 0,
- sizeof(sb.extension_list));
+ set_sb(extension_count, 0);
+ memset(sb->extension_list, 0, sizeof(sb->extension_list));
while (*extlist) {
name_len = strlen(*extlist);
- memcpy(sb.extension_list[i++], *extlist, name_len);
+ memcpy(sb->extension_list[i++], *extlist, name_len);
extlist++;
}
set_sb(extension_count, i);
@@ -141,18 +97,24 @@
return;
/* add user ext list */
- ue = strtok(ext_str, ",");
+ ue = strtok(ext_str, ", ");
while (ue != NULL) {
name_len = strlen(ue);
- memcpy(sb.extension_list[i++], ue, name_len);
- ue = strtok(NULL, ",");
+ if (name_len >= 8) {
+ MSG(0, "\tWarn: Extension name (%s) is too long\n", ue);
+ goto next;
+ }
+ if (!is_extension_exist(ue))
+ memcpy(sb->extension_list[i++], ue, name_len);
+next:
+ ue = strtok(NULL, ", ");
if (i >= F2FS_MAX_EXTENSION)
break;
}
set_sb(extension_count, i);
- free(config.extension_list);
+ free(c.extension_list);
}
static int f2fs_prepare_super_block(void)
@@ -164,19 +126,21 @@
u_int32_t sit_segments;
u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
u_int32_t total_valid_blks_available;
- u_int64_t zone_align_start_offset, diff, total_meta_segments;
+ u_int64_t zone_align_start_offset, diff;
+ u_int64_t total_meta_zones, total_meta_segments;
u_int32_t sit_bitmap_size, max_sit_bitmap_size;
u_int32_t max_nat_bitmap_size, max_nat_segments;
u_int32_t total_zones;
+ int i;
set_sb(magic, F2FS_SUPER_MAGIC);
set_sb(major_ver, F2FS_MAJOR_VERSION);
set_sb(minor_ver, F2FS_MINOR_VERSION);
- log_sectorsize = log_base_2(config.sector_size);
- log_sectors_per_block = log_base_2(config.sectors_per_blk);
+ log_sectorsize = log_base_2(c.sector_size);
+ log_sectors_per_block = log_base_2(c.sectors_per_blk);
log_blocksize = log_sectorsize + log_sectors_per_block;
- log_blks_per_seg = log_base_2(config.blks_per_seg);
+ log_blks_per_seg = log_base_2(c.blks_per_seg);
set_sb(log_sectorsize, log_sectorsize);
set_sb(log_sectors_per_block, log_sectors_per_block);
@@ -184,45 +148,83 @@
set_sb(log_blocksize, log_blocksize);
set_sb(log_blocks_per_seg, log_blks_per_seg);
- set_sb(segs_per_sec, config.segs_per_sec);
- set_sb(secs_per_zone, config.secs_per_zone);
+ set_sb(segs_per_sec, c.segs_per_sec);
+ set_sb(secs_per_zone, c.secs_per_zone);
blk_size_bytes = 1 << log_blocksize;
- segment_size_bytes = blk_size_bytes * config.blks_per_seg;
+ segment_size_bytes = blk_size_bytes * c.blks_per_seg;
zone_size_bytes =
- blk_size_bytes * config.secs_per_zone *
- config.segs_per_sec * config.blks_per_seg;
+ blk_size_bytes * c.secs_per_zone *
+ c.segs_per_sec * c.blks_per_seg;
- sb.checksum_offset = 0;
+ set_sb(checksum_offset, 0);
- set_sb(block_count, config.total_sectors >> log_sectors_per_block);
+ set_sb(block_count, c.total_sectors >> log_sectors_per_block);
zone_align_start_offset =
- (config.start_sector * config.sector_size +
+ (c.start_sector * c.sector_size +
2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
zone_size_bytes * zone_size_bytes -
- config.start_sector * config.sector_size;
+ c.start_sector * c.sector_size;
- if (config.start_sector % config.sectors_per_blk) {
- MSG(1, "\tWARN: Align start sector number to the page unit\n");
+ if (c.start_sector % c.sectors_per_blk) {
+ MSG(1, "\t%s: Align start sector number to the page unit\n",
+ c.zoned_mode ? "FAIL" : "WARN");
MSG(1, "\ti.e., start sector: %d, ofs:%d (sects/page: %d)\n",
- config.start_sector,
- config.start_sector % config.sectors_per_blk,
- config.sectors_per_blk);
+ c.start_sector,
+ c.start_sector % c.sectors_per_blk,
+ c.sectors_per_blk);
+ if (c.zoned_mode)
+ return -1;
}
- set_sb(segment_count, (config.total_sectors * config.sector_size -
- zone_align_start_offset) / segment_size_bytes);
-
set_sb(segment0_blkaddr, zone_align_start_offset / blk_size_bytes);
- sb.cp_blkaddr = sb.segment0_blkaddr;
+ sb->cp_blkaddr = sb->segment0_blkaddr;
- MSG(0, "Info: zone aligned segment0 blkaddr: %u\n", get_sb(segment0_blkaddr));
+ MSG(0, "Info: zone aligned segment0 blkaddr: %u\n",
+ get_sb(segment0_blkaddr));
+ if (c.zoned_mode && (get_sb(segment0_blkaddr) + c.start_sector /
+ c.sectors_per_blk) % c.zone_blocks) {
+ MSG(1, "\tError: Unaligned segment0 block address %u\n",
+ get_sb(segment0_blkaddr));
+ return -1;
+ }
+
+ for (i = 0; i < c.ndevs; i++) {
+ if (i == 0) {
+ c.devices[i].total_segments =
+ (c.devices[i].total_sectors *
+ c.sector_size - zone_align_start_offset) /
+ segment_size_bytes;
+ c.devices[i].start_blkaddr = 0;
+ c.devices[i].end_blkaddr = c.devices[i].total_segments *
+ c.blks_per_seg - 1 +
+ sb->segment0_blkaddr;
+ } else {
+ c.devices[i].total_segments =
+ c.devices[i].total_sectors /
+ (c.sectors_per_blk * c.blks_per_seg);
+ c.devices[i].start_blkaddr =
+ c.devices[i - 1].end_blkaddr + 1;
+ c.devices[i].end_blkaddr = c.devices[i].start_blkaddr +
+ c.devices[i].total_segments *
+ c.blks_per_seg - 1;
+ }
+ if (c.ndevs > 1) {
+ memcpy(sb->devs[i].path, c.devices[i].path, MAX_PATH_LEN);
+ sb->devs[i].total_segments =
+ cpu_to_le32(c.devices[i].total_segments);
+ }
+
+ c.total_segments += c.devices[i].total_segments;
+ }
+ set_sb(segment_count, (c.total_segments / c.segs_per_zone *
+ c.segs_per_zone));
set_sb(segment_count_ckpt, F2FS_NUMBER_OF_CHECKPOINT_PACK);
- set_sb(sit_blkaddr, get_sb(segment0_blkaddr) + get_sb(segment_count_ckpt) *
- config.blks_per_seg);
+ set_sb(sit_blkaddr, get_sb(segment0_blkaddr) +
+ get_sb(segment_count_ckpt) * c.blks_per_seg);
blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
@@ -231,13 +233,14 @@
set_sb(segment_count_sit, sit_segments * 2);
set_sb(nat_blkaddr, get_sb(sit_blkaddr) + get_sb(segment_count_sit) *
- config.blks_per_seg);
+ c.blks_per_seg);
total_valid_blks_available = (get_sb(segment_count) -
- (get_sb(segment_count_ckpt) + get_sb(segment_count_sit))) *
- config.blks_per_seg;
+ (get_sb(segment_count_ckpt) +
+ get_sb(segment_count_sit))) * c.blks_per_seg;
- blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+ blocks_for_nat = ALIGN(total_valid_blks_available,
+ NAT_ENTRY_PER_BLOCK);
set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
/*
@@ -255,16 +258,20 @@
/*
* It should be reserved minimum 1 segment for nat.
- * When sit is too large, we should expand cp area. It requires more pages for cp.
+ * When sit is too large, we should expand cp area. It requires more
+ * pages for cp.
*/
if (max_sit_bitmap_size >
- (CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
- max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
+ (CHECKSUM_OFFSET -
+ sizeof(struct f2fs_checkpoint) + 1 - 64)) {
+ max_nat_bitmap_size = CHECKSUM_OFFSET -
+ sizeof(struct f2fs_checkpoint) + 1;
set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
} else {
- max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
+ max_nat_bitmap_size =
+ CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
- max_sit_bitmap_size;
- sb.cp_payload = 0;
+ set_sb(cp_payload, 0);
}
max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg;
@@ -275,16 +282,16 @@
set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
set_sb(ssa_blkaddr, get_sb(nat_blkaddr) + get_sb(segment_count_nat) *
- config.blks_per_seg);
+ c.blks_per_seg);
total_valid_blks_available = (get_sb(segment_count) -
(get_sb(segment_count_ckpt) +
get_sb(segment_count_sit) +
get_sb(segment_count_nat))) *
- config.blks_per_seg;
+ c.blks_per_seg;
blocks_for_ssa = total_valid_blks_available /
- config.blks_per_seg + 1;
+ c.blks_per_seg + 1;
set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
@@ -292,77 +299,102 @@
get_sb(segment_count_sit) +
get_sb(segment_count_nat) +
get_sb(segment_count_ssa);
- diff = total_meta_segments % (config.segs_per_zone);
+ diff = total_meta_segments % (c.segs_per_zone);
if (diff)
set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
- (config.segs_per_zone - diff));
+ (c.segs_per_zone - diff));
- set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
- config.blks_per_seg);
+ total_meta_zones = ZONE_ALIGN(total_meta_segments *
+ c.blks_per_seg);
- set_sb(segment_count_main, get_sb(segment_count) -
- (get_sb(segment_count_ckpt) +
- get_sb(segment_count_sit) +
- get_sb(segment_count_nat) +
- get_sb(segment_count_ssa)));
+ set_sb(main_blkaddr, get_sb(segment0_blkaddr) + total_meta_zones *
+ c.segs_per_zone * c.blks_per_seg);
- set_sb(section_count, get_sb(segment_count_main) / config.segs_per_sec);
+ if (c.zoned_mode) {
+ /*
+ * Make sure there is enough randomly writeable
+ * space at the beginning of the disk.
+ */
+ unsigned long main_blkzone = get_sb(main_blkaddr) / c.zone_blocks;
- set_sb(segment_count_main, get_sb(section_count) * config.segs_per_sec);
+ if (c.devices[0].zoned_model == F2FS_ZONED_HM &&
+ c.devices[0].nr_rnd_zones < main_blkzone) {
+ MSG(0, "\tError: Device does not have enough random "
+ "write zones for F2FS volume (%lu needed)\n",
+ main_blkzone);
+ return -1;
+ }
+ }
- if ((get_sb(segment_count_main) - 2) <
- config.reserved_segments) {
- MSG(1, "\tError: Device size is not sufficient for F2FS volume,\
- more segment needed =%u",
- config.reserved_segments -
- (get_sb(segment_count_main) - 2));
+ total_zones = get_sb(segment_count) / (c.segs_per_zone) -
+ total_meta_zones;
+
+ set_sb(section_count, total_zones * c.secs_per_zone);
+
+ set_sb(segment_count_main, get_sb(section_count) * c.segs_per_sec);
+
+ /* Let's determine the best reserved and overprovisioned space */
+ if (c.overprovision == 0)
+ c.overprovision = get_best_overprovision(sb);
+
+ c.reserved_segments =
+ (2 * (100 / c.overprovision + 1) + 6)
+ * c.segs_per_sec;
+
+ if (c.overprovision == 0 || c.total_segments < F2FS_MIN_SEGMENTS ||
+ (c.devices[0].total_sectors *
+ c.sector_size < zone_align_start_offset) ||
+ (get_sb(segment_count_main) - 2) < c.reserved_segments) {
+ MSG(0, "\tError: Device size is not sufficient for F2FS volume\n");
return -1;
}
- uuid_generate(sb.uuid);
+ uuid_generate(sb->uuid);
- ASCIIToUNICODE(sb.volume_name, (u_int8_t *)config.vol_label);
-
+ utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
+ MAX_VOLUME_NAME, strlen(c.vol_label));
set_sb(node_ino, 1);
set_sb(meta_ino, 2);
set_sb(root_ino, 3);
- total_zones = get_sb(segment_count_main) / (config.segs_per_zone);
if (total_zones <= 6) {
- MSG(1, "\tError: %d zones: Need more zones \
- by shrinking zone size\n", total_zones);
+ MSG(1, "\tError: %d zones: Need more zones "
+ "by shrinking zone size\n", total_zones);
return -1;
}
- if (config.heap) {
- config.cur_seg[CURSEG_HOT_NODE] = last_section(last_zone(total_zones));
- config.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
- config.cur_seg[CURSEG_COLD_NODE] = prev_zone(CURSEG_WARM_NODE);
- config.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE);
- config.cur_seg[CURSEG_COLD_DATA] = 0;
- config.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
+ if (c.heap) {
+ c.cur_seg[CURSEG_HOT_NODE] =
+ last_section(last_zone(total_zones));
+ c.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
+ c.cur_seg[CURSEG_COLD_NODE] = prev_zone(CURSEG_WARM_NODE);
+ c.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE);
+ c.cur_seg[CURSEG_COLD_DATA] = 0;
+ c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
} else {
- config.cur_seg[CURSEG_HOT_NODE] = 0;
- config.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
- config.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE);
- config.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE);
- config.cur_seg[CURSEG_COLD_DATA] = next_zone(CURSEG_HOT_DATA);
- config.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
+ c.cur_seg[CURSEG_HOT_NODE] = 0;
+ c.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
+ c.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE);
+ c.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE);
+ c.cur_seg[CURSEG_COLD_DATA] = next_zone(CURSEG_HOT_DATA);
+ c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
}
- configure_extension_list();
+ cure_extension_list();
/* get kernel version */
- if (config.kd >= 0) {
- dev_read_version(config.version, 0, VERSION_LEN);
- get_kernel_version(config.version);
- MSG(0, "Info: format version with\n \"%s\"\n", config.version);
+ if (c.kd >= 0) {
+ dev_read_version(c.version, 0, VERSION_LEN);
+ get_kernel_version(c.version);
+ MSG(0, "Info: format version with\n \"%s\"\n", c.version);
} else {
- memset(config.version, 0, VERSION_LEN);
+ memset(c.version, 0, VERSION_LEN);
}
- memcpy(sb.version, config.version, VERSION_LEN);
- memcpy(sb.init_version, config.version, VERSION_LEN);
+ memcpy(sb->version, c.version, VERSION_LEN);
+ memcpy(sb->init_version, c.version, VERSION_LEN);
+
+ sb->feature = c.feature;
return 0;
}
@@ -389,8 +421,8 @@
DBG(1, "\tFilling sit area at offset 0x%08"PRIx64"\n", sit_seg_addr);
for (index = 0; index < (get_sb(segment_count_sit) / 2); index++) {
if (dev_fill(zero_buf, sit_seg_addr, seg_size)) {
- MSG(1, "\tError: While zeroing out the sit area \
- on disk!!!\n");
+ MSG(1, "\tError: While zeroing out the sit area "
+ "on disk!!!\n");
free(zero_buf);
return -1;
}
@@ -423,8 +455,8 @@
DBG(1, "\tFilling nat area at offset 0x%08"PRIx64"\n", nat_seg_addr);
for (index = 0; index < get_sb(segment_count_nat) / 2; index++) {
if (dev_fill(nat_buf, nat_seg_addr, seg_size)) {
- MSG(1, "\tError: While zeroing out the nat area \
- on disk!!!\n");
+ MSG(1, "\tError: While zeroing out the nat area "
+ "on disk!!!\n");
free(nat_buf);
return -1;
}
@@ -438,8 +470,9 @@
static int f2fs_write_check_point_pack(void)
{
struct f2fs_summary_block *sum = NULL;
+ struct f2fs_journal *journal;
u_int32_t blk_size_bytes;
- u_int64_t cp_seg_blk_offset = 0;
+ u_int64_t cp_seg_blk = 0;
u_int32_t crc = 0;
unsigned int i;
char *cp_payload = NULL;
@@ -460,7 +493,7 @@
}
sum_compact = calloc(F2FS_BLKSIZE, 1);
- if (sum == NULL) {
+ if (sum_compact == NULL) {
MSG(1, "\tError: Calloc Failed for summay buffer!!!\n");
goto free_sum;
}
@@ -473,13 +506,13 @@
}
/* 1. cp page 1 of checkpoint pack 1 */
- set_cp(checkpoint_ver, 1);
- set_cp(cur_node_segno[0], config.cur_seg[CURSEG_HOT_NODE]);
- set_cp(cur_node_segno[1], config.cur_seg[CURSEG_WARM_NODE]);
- set_cp(cur_node_segno[2], config.cur_seg[CURSEG_COLD_NODE]);
- set_cp(cur_data_segno[0], config.cur_seg[CURSEG_HOT_DATA]);
- set_cp(cur_data_segno[1], config.cur_seg[CURSEG_WARM_DATA]);
- set_cp(cur_data_segno[2], config.cur_seg[CURSEG_COLD_DATA]);
+ cp->checkpoint_ver = rand() | 0x1;
+ set_cp(cur_node_segno[0], c.cur_seg[CURSEG_HOT_NODE]);
+ set_cp(cur_node_segno[1], c.cur_seg[CURSEG_WARM_NODE]);
+ set_cp(cur_node_segno[2], c.cur_seg[CURSEG_COLD_NODE]);
+ set_cp(cur_data_segno[0], c.cur_seg[CURSEG_HOT_DATA]);
+ set_cp(cur_data_segno[1], c.cur_seg[CURSEG_WARM_DATA]);
+ set_cp(cur_data_segno[2], c.cur_seg[CURSEG_COLD_DATA]);
for (i = 3; i < MAX_ACTIVE_NODE_LOGS; i++) {
set_cp(cur_node_segno[i], 0xffffffff);
set_cp(cur_data_segno[i], 0xffffffff);
@@ -488,17 +521,22 @@
set_cp(cur_node_blkoff[0], 1);
set_cp(cur_data_blkoff[0], 1);
set_cp(valid_block_count, 2);
- set_cp(rsvd_segment_count, config.reserved_segments);
+ set_cp(rsvd_segment_count, c.reserved_segments);
set_cp(overprov_segment_count, (get_sb(segment_count_main) -
get_cp(rsvd_segment_count)) *
- config.overprovision / 100);
+ c.overprovision / 100);
set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
get_cp(rsvd_segment_count));
+ MSG(0, "Info: Overprovision ratio = %.3lf%%\n", c.overprovision);
+ MSG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n",
+ get_cp(overprov_segment_count),
+ c.reserved_segments);
+
/* main segments - reserved segments - (node + data segments) */
set_cp(free_segment_count, get_sb(segment_count_main) - 6);
set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
- get_cp(overprov_segment_count)) * config.blks_per_seg));
+ get_cp(overprov_segment_count)) * c.blks_per_seg));
/* cp page (2), data summaries (1), node summaries (3) */
set_cp(cp_pack_total_block_count, 6 + get_sb(cp_payload));
set_cp(ckpt_flags, CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG);
@@ -519,20 +557,27 @@
cpu_to_le32(crc);
blk_size_bytes = 1 << get_sb(log_blocksize);
- cp_seg_blk_offset = get_sb(segment0_blkaddr);
- cp_seg_blk_offset *= blk_size_bytes;
- DBG(1, "\tWriting main segments, cp at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
- if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+ if (blk_size_bytes != F2FS_BLKSIZE) {
+ MSG(1, "\tError: Wrong block size %d / %d!!!\n",
+ blk_size_bytes, F2FS_BLKSIZE);
+ goto free_cp_payload;
+ }
+
+ cp_seg_blk = get_sb(segment0_blkaddr);
+
+ DBG(1, "\tWriting main segments, cp at offset 0x%08"PRIx64"\n",
+ cp_seg_blk);
+ if (dev_write_block(cp, cp_seg_blk)) {
MSG(1, "\tError: While writing the cp to disk!!!\n");
goto free_cp_payload;
}
for (i = 0; i < get_sb(cp_payload); i++) {
- cp_seg_blk_offset += blk_size_bytes;
- if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) {
- MSG(1, "\tError: While zeroing out the sit bitmap area \
- on disk!!!\n");
+ cp_seg_blk++;
+ if (dev_fill_block(cp_payload, cp_seg_blk)) {
+ MSG(1, "\tError: While zeroing out the sit bitmap area "
+ "on disk!!!\n");
goto free_cp_payload;
}
}
@@ -555,51 +600,58 @@
memset(sum, 0, sizeof(struct f2fs_summary_block));
SET_SUM_TYPE((&sum->footer), SUM_TYPE_DATA);
- sum->n_nats = cpu_to_le16(1);
- sum->nat_j.entries[0].nid = sb.root_ino;
- sum->nat_j.entries[0].ne.version = 0;
- sum->nat_j.entries[0].ne.ino = sb.root_ino;
- sum->nat_j.entries[0].ne.block_addr = cpu_to_le32(
+ journal = &sum->journal;
+ journal->n_nats = cpu_to_le16(1);
+ journal->nat_j.entries[0].nid = sb->root_ino;
+ journal->nat_j.entries[0].ne.version = 0;
+ journal->nat_j.entries[0].ne.ino = sb->root_ino;
+ journal->nat_j.entries[0].ne.block_addr = cpu_to_le32(
get_sb(main_blkaddr) +
- get_cp(cur_node_segno[0]) * config.blks_per_seg);
+ get_cp(cur_node_segno[0]) * c.blks_per_seg);
- memcpy(sum_compact_p, &sum->n_nats, SUM_JOURNAL_SIZE);
+ memcpy(sum_compact_p, &journal->n_nats, SUM_JOURNAL_SIZE);
sum_compact_p += SUM_JOURNAL_SIZE;
memset(sum, 0, sizeof(struct f2fs_summary_block));
/* inode sit for root */
- sum->n_sits = cpu_to_le16(6);
- sum->sit_j.entries[0].segno = cp->cur_node_segno[0];
- sum->sit_j.entries[0].se.vblocks = cpu_to_le16((CURSEG_HOT_NODE << 10) | 1);
- f2fs_set_bit(0, (char *)sum->sit_j.entries[0].se.valid_map);
- sum->sit_j.entries[1].segno = cp->cur_node_segno[1];
- sum->sit_j.entries[1].se.vblocks = cpu_to_le16((CURSEG_WARM_NODE << 10));
- sum->sit_j.entries[2].segno = cp->cur_node_segno[2];
- sum->sit_j.entries[2].se.vblocks = cpu_to_le16((CURSEG_COLD_NODE << 10));
+ journal->n_sits = cpu_to_le16(6);
+ journal->sit_j.entries[0].segno = cp->cur_node_segno[0];
+ journal->sit_j.entries[0].se.vblocks =
+ cpu_to_le16((CURSEG_HOT_NODE << 10) | 1);
+ f2fs_set_bit(0, (char *)journal->sit_j.entries[0].se.valid_map);
+ journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
+ journal->sit_j.entries[1].se.vblocks =
+ cpu_to_le16((CURSEG_WARM_NODE << 10));
+ journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
+ journal->sit_j.entries[2].se.vblocks =
+ cpu_to_le16((CURSEG_COLD_NODE << 10));
/* data sit for root */
- sum->sit_j.entries[3].segno = cp->cur_data_segno[0];
- sum->sit_j.entries[3].se.vblocks = cpu_to_le16((CURSEG_HOT_DATA << 10) | 1);
- f2fs_set_bit(0, (char *)sum->sit_j.entries[3].se.valid_map);
- sum->sit_j.entries[4].segno = cp->cur_data_segno[1];
- sum->sit_j.entries[4].se.vblocks = cpu_to_le16((CURSEG_WARM_DATA << 10));
- sum->sit_j.entries[5].segno = cp->cur_data_segno[2];
- sum->sit_j.entries[5].se.vblocks = cpu_to_le16((CURSEG_COLD_DATA << 10));
+ journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
+ journal->sit_j.entries[3].se.vblocks =
+ cpu_to_le16((CURSEG_HOT_DATA << 10) | 1);
+ f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
+ journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
+ journal->sit_j.entries[4].se.vblocks =
+ cpu_to_le16((CURSEG_WARM_DATA << 10));
+ journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
+ journal->sit_j.entries[5].se.vblocks =
+ cpu_to_le16((CURSEG_COLD_DATA << 10));
- memcpy(sum_compact_p, &sum->n_sits, SUM_JOURNAL_SIZE);
+ memcpy(sum_compact_p, &journal->n_sits, SUM_JOURNAL_SIZE);
sum_compact_p += SUM_JOURNAL_SIZE;
/* hot data summary */
sum_entry = (struct f2fs_summary *)sum_compact_p;
- sum_entry->nid = sb.root_ino;
+ sum_entry->nid = sb->root_ino;
sum_entry->ofs_in_node = 0;
/* warm data summary, nothing to do */
/* cold data summary, nothing to do */
- cp_seg_blk_offset += blk_size_bytes;
+ cp_seg_blk++;
DBG(1, "\tWriting Segment summary for HOT/WARM/COLD_DATA, at offset 0x%08"PRIx64"\n",
- cp_seg_blk_offset);
- if (dev_write(sum_compact, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk);
+ if (dev_write_block(sum_compact, cp_seg_blk)) {
MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
goto free_cp_payload;
}
@@ -608,13 +660,13 @@
memset(sum, 0, sizeof(struct f2fs_summary_block));
SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
- sum->entries[0].nid = sb.root_ino;
+ sum->entries[0].nid = sb->root_ino;
sum->entries[0].ofs_in_node = 0;
- cp_seg_blk_offset += blk_size_bytes;
+ cp_seg_blk++;
DBG(1, "\tWriting Segment summary for HOT_NODE, at offset 0x%08"PRIx64"\n",
- cp_seg_blk_offset);
- if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk);
+ if (dev_write_block(sum, cp_seg_blk)) {
MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
goto free_cp_payload;
}
@@ -623,10 +675,10 @@
memset(sum, 0, sizeof(struct f2fs_summary_block));
SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
- cp_seg_blk_offset += blk_size_bytes;
+ cp_seg_blk++;
DBG(1, "\tWriting Segment summary for WARM_NODE, at offset 0x%08"PRIx64"\n",
- cp_seg_blk_offset);
- if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk);
+ if (dev_write_block(sum, cp_seg_blk)) {
MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
goto free_cp_payload;
}
@@ -634,18 +686,18 @@
/* Fill segment summary for COLD_NODE to zero. */
memset(sum, 0, sizeof(struct f2fs_summary_block));
SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE);
- cp_seg_blk_offset += blk_size_bytes;
+ cp_seg_blk++;
DBG(1, "\tWriting Segment summary for COLD_NODE, at offset 0x%08"PRIx64"\n",
- cp_seg_blk_offset);
- if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk);
+ if (dev_write_block(sum, cp_seg_blk)) {
MSG(1, "\tError: While writing the sum_blk to disk!!!\n");
goto free_cp_payload;
}
/* cp page2 */
- cp_seg_blk_offset += blk_size_bytes;
- DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
- if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk++;
+ DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk);
+ if (dev_write_block(cp, cp_seg_blk)) {
MSG(1, "\tError: While writing the cp to disk!!!\n");
goto free_cp_payload;
}
@@ -658,29 +710,29 @@
crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) =
cpu_to_le32(crc);
- cp_seg_blk_offset = (get_sb(segment0_blkaddr) +
- config.blks_per_seg) *
- blk_size_bytes;
- DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
- if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk = get_sb(segment0_blkaddr) + c.blks_per_seg;
+ DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n",
+ cp_seg_blk);
+ if (dev_write_block(cp, cp_seg_blk)) {
MSG(1, "\tError: While writing the cp to disk!!!\n");
goto free_cp_payload;
}
for (i = 0; i < get_sb(cp_payload); i++) {
- cp_seg_blk_offset += blk_size_bytes;
- if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) {
- MSG(1, "\tError: While zeroing out the sit bitmap area \
- on disk!!!\n");
+ cp_seg_blk++;
+ if (dev_fill_block(cp_payload, cp_seg_blk)) {
+ MSG(1, "\tError: While zeroing out the sit bitmap area "
+ "on disk!!!\n");
goto free_cp_payload;
}
}
/* cp page 2 of check point pack 2 */
- cp_seg_blk_offset += blk_size_bytes * (le32_to_cpu(cp->cp_pack_total_block_count)
- - get_sb(cp_payload) - 1);
- DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset);
- if (dev_write(cp, cp_seg_blk_offset, blk_size_bytes)) {
+ cp_seg_blk += (le32_to_cpu(cp->cp_pack_total_block_count) -
+ get_sb(cp_payload) - 1);
+ DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n",
+ cp_seg_blk);
+ if (dev_write_block(cp, cp_seg_blk)) {
MSG(1, "\tError: While writing the cp to disk!!!\n");
goto free_cp_payload;
}
@@ -705,13 +757,12 @@
zero_buff = calloc(F2FS_BLKSIZE, 1);
- memcpy(zero_buff + F2FS_SUPER_OFFSET, &sb,
- sizeof(sb));
+ memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb));
DBG(1, "\tWriting super block, at offset 0x%08x\n", 0);
for (index = 0; index < 2; index++) {
- if (dev_write(zero_buff, index * F2FS_BLKSIZE, F2FS_BLKSIZE)) {
- MSG(1, "\tError: While while writing supe_blk \
- on disk!!! index : %d\n", index);
+ if (dev_write_block(zero_buff, index)) {
+ MSG(1, "\tError: While while writing supe_blk "
+ "on disk!!! index : %d\n", index);
free(zero_buff);
return -1;
}
@@ -721,6 +772,35 @@
return 0;
}
+#ifndef WITH_ANDROID
+static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
+{
+ if (c.zoned_mode)
+ return 0;
+ do {
+ if (offset < get_sb(main_blkaddr) ||
+ offset >= get_sb(main_blkaddr) + get_sb(block_count))
+ break;
+
+ if (dev_read_block(raw_node, offset)) {
+ MSG(1, "\tError: While traversing direct node!!!\n");
+ return -1;
+ }
+
+ memset(raw_node, 0, F2FS_BLKSIZE);
+
+ DBG(1, "\tDiscard dnode, at offset 0x%08"PRIx64"\n", offset);
+ if (dev_write_block(raw_node, offset)) {
+ MSG(1, "\tError: While discarding direct node!!!\n");
+ return -1;
+ }
+ offset = le32_to_cpu(raw_node->footer.next_blkaddr);
+ } while (1);
+
+ return 0;
+}
+#endif
+
static int f2fs_write_root_inode(void)
{
struct f2fs_node *raw_node = NULL;
@@ -733,13 +813,13 @@
return -1;
}
- raw_node->footer.nid = sb.root_ino;
- raw_node->footer.ino = sb.root_ino;
+ raw_node->footer.nid = sb->root_ino;
+ raw_node->footer.ino = sb->root_ino;
raw_node->footer.cp_ver = cpu_to_le64(1);
raw_node->footer.next_blkaddr = cpu_to_le32(
get_sb(main_blkaddr) +
- config.cur_seg[CURSEG_HOT_NODE] *
- config.blks_per_seg + 1);
+ c.cur_seg[CURSEG_HOT_NODE] *
+ c.blks_per_seg + 1);
raw_node->i.i_mode = cpu_to_le16(0x41ed);
raw_node->i.i_links = cpu_to_le32(2);
@@ -763,39 +843,39 @@
raw_node->i.i_dir_level = DEF_DIR_LEVEL;
data_blk_nor = get_sb(main_blkaddr) +
- config.cur_seg[CURSEG_HOT_DATA] * config.blks_per_seg;
+ c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg;
raw_node->i.i_addr[0] = cpu_to_le32(data_blk_nor);
raw_node->i.i_ext.fofs = 0;
- raw_node->i.i_ext.blk_addr = cpu_to_le32(data_blk_nor);
- raw_node->i.i_ext.len = cpu_to_le32(1);
+ raw_node->i.i_ext.blk_addr = 0;
+ raw_node->i.i_ext.len = 0;
main_area_node_seg_blk_offset = get_sb(main_blkaddr);
- main_area_node_seg_blk_offset += config.cur_seg[CURSEG_HOT_NODE] *
- config.blks_per_seg;
- main_area_node_seg_blk_offset *= blk_size_bytes;
+ main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] *
+ c.blks_per_seg;
- DBG(1, "\tWriting root inode (hot node), at offset 0x%08"PRIx64"\n", main_area_node_seg_blk_offset);
- if (dev_write(raw_node, main_area_node_seg_blk_offset, F2FS_BLKSIZE)) {
+ DBG(1, "\tWriting root inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n",
+ get_sb(main_blkaddr),
+ c.cur_seg[CURSEG_HOT_NODE],
+ c.blks_per_seg, main_area_node_seg_blk_offset);
+ if (dev_write_block(raw_node, main_area_node_seg_blk_offset)) {
MSG(1, "\tError: While writing the raw_node to disk!!!\n");
free(raw_node);
return -1;
}
- memset(raw_node, 0xff, sizeof(struct f2fs_node));
-
/* avoid power-off-recovery based on roll-forward policy */
main_area_node_seg_blk_offset = get_sb(main_blkaddr);
- main_area_node_seg_blk_offset += config.cur_seg[CURSEG_WARM_NODE] *
- config.blks_per_seg;
- main_area_node_seg_blk_offset *= blk_size_bytes;
+ main_area_node_seg_blk_offset += c.cur_seg[CURSEG_WARM_NODE] *
+ c.blks_per_seg;
- DBG(1, "\tWriting root inode (warm node), at offset 0x%08"PRIx64"\n", main_area_node_seg_blk_offset);
- if (dev_write(raw_node, main_area_node_seg_blk_offset, F2FS_BLKSIZE)) {
- MSG(1, "\tError: While writing the raw_node to disk!!!\n");
+#ifndef WITH_ANDROID
+ if (discard_obsolete_dnode(raw_node, main_area_node_seg_blk_offset)) {
free(raw_node);
return -1;
}
+#endif
+
free(raw_node);
return 0;
}
@@ -803,7 +883,7 @@
static int f2fs_update_nat_root(void)
{
struct f2fs_nat_block *nat_blk = NULL;
- u_int64_t blk_size_bytes, nat_seg_blk_offset = 0;
+ u_int64_t nat_seg_blk_offset = 0;
nat_blk = calloc(F2FS_BLKSIZE, 1);
if(nat_blk == NULL) {
@@ -814,23 +894,22 @@
/* update root */
nat_blk->entries[get_sb(root_ino)].block_addr = cpu_to_le32(
get_sb(main_blkaddr) +
- config.cur_seg[CURSEG_HOT_NODE] * config.blks_per_seg);
- nat_blk->entries[get_sb(root_ino)].ino = sb.root_ino;
+ c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg);
+ nat_blk->entries[get_sb(root_ino)].ino = sb->root_ino;
/* update node nat */
nat_blk->entries[get_sb(node_ino)].block_addr = cpu_to_le32(1);
- nat_blk->entries[get_sb(node_ino)].ino = sb.node_ino;
+ nat_blk->entries[get_sb(node_ino)].ino = sb->node_ino;
/* update meta nat */
nat_blk->entries[get_sb(meta_ino)].block_addr = cpu_to_le32(1);
- nat_blk->entries[get_sb(meta_ino)].ino = sb.meta_ino;
+ nat_blk->entries[get_sb(meta_ino)].ino = sb->meta_ino;
- blk_size_bytes = 1 << get_sb(log_blocksize);
nat_seg_blk_offset = get_sb(nat_blkaddr);
- nat_seg_blk_offset *= blk_size_bytes;
- DBG(1, "\tWriting nat root, at offset 0x%08"PRIx64"\n", nat_seg_blk_offset);
- if (dev_write(nat_blk, nat_seg_blk_offset, F2FS_BLKSIZE)) {
+ DBG(1, "\tWriting nat root, at offset 0x%08"PRIx64"\n",
+ nat_seg_blk_offset);
+ if (dev_write_block(nat_blk, nat_seg_blk_offset)) {
MSG(1, "\tError: While writing the nat_blk set0 to disk!\n");
free(nat_blk);
return -1;
@@ -843,7 +922,7 @@
static int f2fs_add_default_dentry_root(void)
{
struct f2fs_dentry_block *dent_blk = NULL;
- u_int64_t blk_size_bytes, data_blk_offset = 0;
+ u_int64_t data_blk_offset = 0;
dent_blk = calloc(F2FS_BLKSIZE, 1);
if(dent_blk == NULL) {
@@ -852,27 +931,27 @@
}
dent_blk->dentry[0].hash_code = 0;
- dent_blk->dentry[0].ino = sb.root_ino;
+ dent_blk->dentry[0].ino = sb->root_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 = sb.root_ino;
+ dent_blk->dentry[1].ino = sb->root_ino;
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);
/* bitmap for . and .. */
- dent_blk->dentry_bitmap[0] = (1 << 1) | (1 << 0);
- blk_size_bytes = 1 << get_sb(log_blocksize);
+ test_and_set_bit_le(0, dent_blk->dentry_bitmap);
+ test_and_set_bit_le(1, dent_blk->dentry_bitmap);
data_blk_offset = get_sb(main_blkaddr);
- data_blk_offset += config.cur_seg[CURSEG_HOT_DATA] *
- config.blks_per_seg;
- data_blk_offset *= blk_size_bytes;
+ data_blk_offset += c.cur_seg[CURSEG_HOT_DATA] *
+ c.blks_per_seg;
- DBG(1, "\tWriting default dentry root, at offset 0x%08"PRIx64"\n", data_blk_offset);
- if (dev_write(dent_blk, data_blk_offset, F2FS_BLKSIZE)) {
+ DBG(1, "\tWriting default dentry root, at offset 0x%08"PRIx64"\n",
+ data_blk_offset);
+ if (dev_write_block(dent_blk, data_blk_offset)) {
MSG(1, "\tError: While writing the dentry_blk to disk!!!\n");
free(dent_blk);
return -1;
@@ -920,10 +999,12 @@
goto exit;
}
- err = f2fs_trim_device();
- if (err < 0) {
- MSG(0, "\tError: Failed to trim whole device!!!\n");
- goto exit;
+ if (c.trim) {
+ err = f2fs_trim_devices();
+ if (err < 0) {
+ MSG(0, "\tError: Failed to trim whole device!!!\n");
+ goto exit;
+ }
}
err = f2fs_init_sit_area();
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index bba2085..5bb1faf 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -16,76 +16,117 @@
#include <sys/stat.h>
#include <sys/mount.h>
#include <time.h>
-//#include <linux/fs.h>
#include <uuid/uuid.h>
#include "f2fs_fs.h"
#include "f2fs_format_utils.h"
-extern struct f2fs_configuration config;
+extern struct f2fs_configuration c;
static void mkfs_usage()
{
MSG(0, "\nUsage: mkfs.f2fs [options] device [sectors]\n");
MSG(0, "[options]:\n");
MSG(0, " -a heap-based allocation [default:1]\n");
+ MSG(0, " -c [device path] up to 7 devices excepts meta device\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -e [extension list] e.g. \"mp3,gif,mov\"\n");
MSG(0, " -l label\n");
MSG(0, " -o overprovision ratio [default:5]\n");
+ MSG(0, " -O set feature\n");
+ MSG(0, " -q quiet mode\n");
MSG(0, " -s # of segments per section [default:1]\n");
MSG(0, " -z # of sections per zone [default:1]\n");
MSG(0, " -t 0: nodiscard, 1: discard [default:1]\n");
+ MSG(0, " -m support zoned block device [default:0]\n");
MSG(0, "sectors: number of sectors. [default: determined by device size]\n");
exit(1);
}
+static void f2fs_show_info()
+{
+ MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
+ F2FS_TOOLS_VERSION,
+ F2FS_TOOLS_DATE);
+ if (c.heap == 0)
+ MSG(0, "Info: Disable heap-based policy\n");
+
+ MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
+ if (c.extension_list)
+ MSG(0, "Info: Add new extension list\n");
+
+ if (c.vol_label)
+ MSG(0, "Info: Label = %s\n", c.vol_label);
+ MSG(0, "Info: Trim is %s\n", c.trim ? "enabled": "disabled");
+}
+
+static void parse_feature(const char *features)
+{
+ if (!strcmp(features, "encrypt")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
+ } else {
+ MSG(0, "Error: Wrong features\n");
+ mkfs_usage();
+ }
+}
+
static void f2fs_parse_options(int argc, char *argv[])
{
- static const char *option_string = "a:d:e:l:o:s:z:t:";
+ static const char *option_string = "qa:c:d:e:l:mo:O:s:z:t:";
int32_t option=0;
while ((option = getopt(argc,argv,option_string)) != EOF) {
switch (option) {
+ case 'q':
+ c.dbg_lv = -1;
+ break;
case 'a':
- config.heap = atoi(optarg);
- if (config.heap == 0)
- MSG(0, "Info: Disable heap-based policy\n");
+ c.heap = atoi(optarg);
+ break;
+ case 'c':
+ if (c.ndevs >= MAX_DEVICES) {
+ MSG(0, "Error: Too many devices\n");
+ mkfs_usage();
+ }
+
+ if (strlen(optarg) > MAX_PATH_LEN) {
+ MSG(0, "Error: device path should be less than "
+ "%d characters\n", MAX_PATH_LEN);
+ mkfs_usage();
+ }
+ c.devices[c.ndevs++].path = strdup(optarg);
break;
case 'd':
- config.dbg_lv = atoi(optarg);
- MSG(0, "Info: Debug level = %d\n", config.dbg_lv);
+ c.dbg_lv = atoi(optarg);
break;
case 'e':
- config.extension_list = strdup(optarg);
- MSG(0, "Info: Add new extension list\n");
+ c.extension_list = strdup(optarg);
break;
case 'l': /*v: volume label */
if (strlen(optarg) > 512) {
- MSG(0, "Error: Volume Label should be less than\
- 512 characters\n");
+ MSG(0, "Error: Volume Label should be less than "
+ "512 characters\n");
mkfs_usage();
}
- config.vol_label = optarg;
- MSG(0, "Info: Label = %s\n", config.vol_label);
+ c.vol_label = optarg;
+ break;
+ case 'm':
+ c.zoned_mode = 1;
break;
case 'o':
- config.overprovision = atoi(optarg);
- MSG(0, "Info: Overprovision ratio = %u%%\n",
- atoi(optarg));
+ c.overprovision = atof(optarg);
+ break;
+ case 'O':
+ parse_feature(optarg);
break;
case 's':
- config.segs_per_sec = atoi(optarg);
- MSG(0, "Info: Segments per section = %d\n",
- atoi(optarg));
+ c.segs_per_sec = atoi(optarg);
break;
case 'z':
- config.secs_per_zone = atoi(optarg);
- MSG(0, "Info: Sections per zone = %d\n", atoi(optarg));
+ c.secs_per_zone = atoi(optarg);
break;
case 't':
- config.trim = atoi(optarg);
- MSG(0, "Info: Trim is %s\n", config.trim ? "enabled": "disabled");
+ c.trim = atoi(optarg);
break;
default:
MSG(0, "\tError: Unknown option %c\n",option);
@@ -98,40 +139,56 @@
MSG(0, "\tError: Device not specified\n");
mkfs_usage();
}
- config.device_name = argv[optind];
+
+ /* [0] : META, [1 to MAX_DEVICES - 1] : NODE/DATA */
+ c.devices[0].path = strdup(argv[optind]);
if ((optind + 1) < argc) {
- /* We have a sector count. */
- config.total_sectors = atoll(argv[optind+1]);
- MSG(0, "\ttotal_sectors=%08"PRIx64" (%s bytes)\n",
- config.total_sectors, argv[optind+1]);
+ if (c.ndevs > 1) {
+ MSG(0, "\tError: Not support custom size on multi-devs.\n");
+ mkfs_usage();
+ }
+ c.wanted_total_sectors = atoll(argv[optind+1]);
}
- config.reserved_segments =
- (2 * (100 / config.overprovision + 1) + 6)
- * config.segs_per_sec;
- config.segs_per_zone = config.segs_per_sec * config.secs_per_zone;
+ if (c.zoned_mode)
+ c.feature |= cpu_to_le32(F2FS_FEATURE_BLKZONED);
}
int main(int argc, char *argv[])
{
- MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
- F2FS_TOOLS_VERSION,
- F2FS_TOOLS_DATE);
- f2fs_init_configuration(&config);
+ f2fs_init_configuration();
f2fs_parse_options(argc, argv);
- if (f2fs_dev_is_umounted(&config) < 0)
+ f2fs_show_info();
+
+ if (f2fs_devs_are_umounted() < 0) {
+ MSG(0, "\tError: Not available on mounted device!\n");
+ return -1;
+ }
+
+ if (f2fs_get_device_info() < 0)
return -1;
- if (f2fs_get_device_info(&config) < 0)
+ /*
+ * Some options are mandatory for host-managed
+ * zoned block devices.
+ */
+ if (c.zoned_model == F2FS_ZONED_HM && !c.zoned_mode) {
+ MSG(0, "\tError: zoned block device feature is required\n");
return -1;
+ }
+
+ if (c.zoned_mode && !c.trim) {
+ MSG(0, "\tError: Trim is required for zoned block devices\n");
+ return -1;
+ }
if (f2fs_format_device() < 0)
return -1;
- f2fs_finalize_device(&config);
+ f2fs_finalize_device();
MSG(0, "Info: format successful\n");
diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c
index a0f85f5..fc80ec6 100644
--- a/mkfs/f2fs_format_utils.c
+++ b/mkfs/f2fs_format_utils.c
@@ -27,38 +27,55 @@
#include <linux/falloc.h>
#endif
-int f2fs_trim_device()
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+static int trim_device(int i)
{
unsigned long long range[2];
struct stat stat_buf;
+ struct device_info *dev = c.devices + i;
+ u_int64_t bytes = dev->total_sectors * dev->sector_size;
+ int fd = dev->fd;
- if (!config.trim)
- return 0;
-
- range[0] = 0;
- range[1] = config.total_sectors * config.sector_size;
-
- if (fstat(config.fd, &stat_buf) < 0 ) {
+ if (fstat(fd, &stat_buf) < 0 ) {
MSG(1, "\tError: Failed to get the device stat!!!\n");
return -1;
}
+ range[0] = 0;
+ range[1] = bytes;
+
#if defined(WITH_BLKDISCARD) && defined(BLKDISCARD)
- MSG(0, "Info: Discarding device\n");
+ MSG(0, "Info: [%s] Discarding device\n", dev->path);
if (S_ISREG(stat_buf.st_mode)) {
-#ifdef FALLOC_FL_PUNCH_HOLE
- if (fallocate(config.fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
+ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
range[0], range[1]) < 0) {
MSG(0, "Info: fallocate(PUNCH_HOLE|KEEP_SIZE) is failed\n");
}
#endif
return 0;
} else if (S_ISBLK(stat_buf.st_mode)) {
- if (ioctl(config.fd, BLKDISCARD, &range) < 0) {
- MSG(0, "Info: This device doesn't support TRIM\n");
+ if (dev->zoned_model != F2FS_ZONED_NONE)
+ return f2fs_reset_zones(i);
+#ifdef BLKSECDISCARD
+ if (ioctl(fd, BLKSECDISCARD, &range) < 0) {
+ MSG(0, "Info: This device doesn't support BLKSECDISCARD\n");
} else {
- MSG(0, "Info: Discarded %lu sectors\n",
- config.total_sectors);
+ MSG(0, "Info: Secure Discarded %lu MB\n",
+ stat_buf.st_size >> 20);
+ return 0;
+ }
+#endif
+ if (ioctl(fd, BLKDISCARD, &range) < 0) {
+ MSG(0, "Info: This device doesn't support BLKDISCARD\n");
+ } else {
+ MSG(0, "Info: Discarded %llu MB\n", range[1] >> 20);
}
} else
return -1;
@@ -66,3 +83,12 @@
return 0;
}
+int f2fs_trim_devices(void)
+{
+ int i;
+
+ for (i = 0; i < c.ndevs; i++)
+ if (trim_device(i))
+ return -1;
+ return 0;
+}
diff --git a/mkfs/f2fs_format_utils.h b/mkfs/f2fs_format_utils.h
index 9eb2cea..274db7b 100644
--- a/mkfs/f2fs_format_utils.h
+++ b/mkfs/f2fs_format_utils.h
@@ -10,7 +10,8 @@
#include "f2fs_fs.h"
-extern struct f2fs_configuration config;
+extern struct f2fs_configuration c;
-int f2fs_trim_device(void);
+int f2fs_trim_device(int, u_int64_t);
+int f2fs_trim_devices(void);
int f2fs_format_device(void);
diff --git a/scripts/verify.sh b/scripts/verify.sh
new file mode 100755
index 0000000..8a22a2d
--- /dev/null
+++ b/scripts/verify.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+
+IMG=../test.img
+TMP=/tmp/res
+XFSTESTS=~/xfstests
+TESTS="4 5 8 11 16 25 32 55 64"
+
+TARGET=./testdir
+MNT=/mnt/resize
+
+mkdir $TARGET 2>/dev/null
+mkdir $MNT 2>/dev/null
+
+umount $TARGET
+umount $MNT
+
+_check_out()
+{
+ if [ $1 -ne 0 ]; then
+ grep ASSERT $TMP
+ echo FAIL RETURN $1
+ exit
+ fi
+}
+
+_get_sec()
+{
+ echo $(($1*1024*1024*1024/512))
+}
+
+_mkfs()
+{
+ echo "========== Initialize $1 GB ============"
+ mkfs.f2fs $IMG `_get_sec $1` | grep sectors
+}
+
+_mount()
+{
+ echo "========== mount to $1 ================="
+ mount -t f2fs -o loop,discard,inline_data,inline_xattr $IMG $1 2>&1
+ _check_out $?
+}
+
+_fsck()
+{
+ echo "========== fsck.f2fs ==================="
+ fsck.f2fs $IMG -t 2>&1 >$TMP
+ _check_out $?
+ grep FSCK $TMP
+}
+
+_fsstress()
+{
+ echo "========== fsstress $1 ================="
+ $XFSTESTS/ltp/fsstress -x "echo 3 > /proc/sys/vm/drop_caches && sleep 1" -X 1 -r -f fsync=8 -f sync=0 -f write=8 -f dwrite=2 -f truncate=6 -f allocsp=0 -f bulkstat=0 -f bulkstat1=0 -f freesp=0 -f zero=1 -f collapse=1 -f insert=1 -f resvsp=0 -f unresvsp=0 -S t -p 10 -n $2 -d $1 >/dev/null
+}
+
+_resize()
+{
+ echo "========== resize.f2fs $1 GB ==========="
+ resize.f2fs -t `_get_sec $1` $IMG 2>&1 >$TMP
+ _check_out $?
+ _fsck
+}
+
+_resize_tests()
+{
+ for i in $TESTS
+ do
+ if [ $i -ge $1 ]; then
+ _resize $i
+ fi
+ done
+}
+
+_sload()
+{
+ echo "========== sload $1 ===================="
+ sload.f2fs -f $1 $IMG 2>&1
+ _check_out $?
+}
+
+from_mount()
+{
+ echo ""
+ echo " **** $1 GB to $2 GB with $3 *** "
+ _mkfs $1
+ _mount $3
+ _fsstress $3 10000
+ umount $3
+ _fsck
+ _resize_tests $2
+}
+
+from_sload()
+{
+ echo ""
+ echo " **** $1 GB to $2 GB with $3 *** "
+
+ _mkfs $1
+ _sload $3
+ _fsck
+
+ _mount $MNT
+ _fsstress $MNT 10000
+ umount $MNT
+ _fsck
+
+ _resize_tests $2
+
+ _mount $MNT
+ _fsstress $MNT 10000
+ umount $MNT
+ _fsck
+}
+
+test_all()
+{
+ for i in $TESTS
+ do
+ for j in $TESTS
+ do
+ if [ $i -lt $j ]; then
+ $1 $i $j $2
+ fi
+ done
+ done
+}
+
+test_all from_sload ~/grub
+
+rm -rf $TARGET/*
+_fsstress $TARGET 5000
+test_all from_sload $TARGET
+rm -rf $TARGET 2>/dev/null
+
+test_all from_mount $MNT
diff --git a/tools/fibmap.c b/tools/fibmap.c
index 224b233..6b092f5 100644
--- a/tools/fibmap.c
+++ b/tools/fibmap.c
@@ -8,6 +8,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <libgen.h>
#include <linux/hdreg.h>
#include <linux/types.h>