[automerger skipped] Merge changes from topic "discard" into oc-dev am: 1cc112035f -s ours
am: 8505f4f182 -s ours
Change-Id: I5d823e82145291fecc0005a183bcd6236a77cb70
diff --git a/.gitignore b/.gitignore
index debeffb..abe1336 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@
/tools/f2fstat
/tools/fibmap.f2fs
/tools/parse.f2fs
+/tools/f2fscrypt
diff --git a/Android.mk b/Android.mk
index afd719b..d4f19cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,19 +5,20 @@
# The versions depend on $(LOCAL_PATH)/VERSION
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)
+common_CFLAGS := -DWITH_ANDROID -DWITH_BLKDISCARD $(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/
+common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/ system/core/libsparse/include
#----------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libf2fs_fmt
LOCAL_SRC_FILES := \
lib/libf2fs.c \
+ lib/libf2fs_zoned.c \
mkfs/f2fs_format.c \
mkfs/f2fs_format_utils.c \
@@ -31,6 +32,7 @@
LOCAL_MODULE := libf2fs_fmt_host
LOCAL_SRC_FILES := \
lib/libf2fs.c \
+ lib/libf2fs_zoned.c \
mkfs/f2fs_format.c \
mkfs/f2fs_format_utils.c \
@@ -44,6 +46,7 @@
LOCAL_MODULE := libf2fs_fmt_host_dyn
LOCAL_SRC_FILES := \
lib/libf2fs.c \
+ lib/libf2fs_zoned.c \
lib/libf2fs_io.c \
mkfs/f2fs_format.c \
mkfs/f2fs_format_utils.c \
@@ -52,10 +55,10 @@
LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
LOCAL_STATIC_LIBRARIES := \
- libf2fs_ioutils_host \
- libext2_uuid \
- libsparse \
- libz
+ libf2fs_ioutils_host \
+ libext2_uuid \
+ libsparse \
+ libz
# LOCAL_LDLIBS := -ldl
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -74,7 +77,11 @@
mkfs/f2fs_format_main.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_STATIC_LIBRARIES := libc libf2fs_fmt libext2_uuid
+LOCAL_STATIC_LIBRARIES := \
+ libf2fs_fmt \
+ libext2_uuid \
+ libsparse \
+ libz
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
@@ -88,13 +95,27 @@
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS := $(target_CFLAGS)
LOCAL_STATIC_LIBRARIES := libf2fs_fmt
-LOCAL_SHARED_LIBRARIES := libext2_uuid
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
#----------------------------------------------------------
include $(CLEAR_VARS)
+LOCAL_MODULE := make_f2fs
+
+LOCAL_SRC_FILES := \
+ mkfs/f2fs_format_main.c \
+ lib/libf2fs_io.c \
+
+LOCAL_C_INCLUDES := $(common_C_INCLUDES)
+LOCAL_CFLAGS := $(common_CFLAGS)
+LOCAL_STATIC_LIBRARIES := libf2fs_fmt_host
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
+include $(BUILD_HOST_EXECUTABLE)
+
+#----------------------------------------------------------
+include $(CLEAR_VARS)
# The LOCAL_MODULE name is referenced by the code. Don't change it.
LOCAL_MODULE := fsck.f2fs
LOCAL_SRC_FILES := \
@@ -107,7 +128,7 @@
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_SHARED_LIBRARIES := libext2_uuid
+LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
@@ -125,6 +146,7 @@
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS := $(common_CFLAGS)
+LOCAL_SHARED_LIBRARIES := libsparse
LOCAL_HOST_SHARED_LIBRARIES := libext2_uuid
include $(BUILD_HOST_EXECUTABLE)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..8256d56
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+deymo@google.com
+jaegeuk@google.com
+jinqian@google.com
diff --git a/configure.ac b/configure.ac
index 6a3f7c4..d6de43b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,8 +48,11 @@
# Test configure options.
AC_ARG_WITH([selinux],
AS_HELP_STRING([--without-selinux],
- [Ignore presence of libselinux and disable selinux support])
-)
+ [Ignore presence of libselinux and disable selinux support]))
+
+AC_ARG_WITH([blkid],
+ AS_HELP_STRING([--without-blkid],
+ [Ignore presence of libblkid and disable blkid support]))
# Checks for programs.
AC_PROG_CC
@@ -74,6 +77,19 @@
)]
)
+AS_IF([test "x$with_blkid" != "xno"],
+ [PKG_CHECK_MODULES([libblkid], [blkid],
+ [have_blkid=yes], [have_blkid=no])],
+ [have_blkid=no]
+)
+
+AS_IF([test "x$have_blkid" = "xyes"],
+ [AC_DEFINE([HAVE_LIBBLKID], [1], [Use blkid])],
+ [AS_IF([test "x$with_blkid" = "xyes"],
+ [AC_MSG_ERROR([blkid support requested but libblkid not found])]
+ )]
+)
+
# Checks for header files.
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])
diff --git a/fsck/dir.c b/fsck/dir.c
index d817d27..57b7f9b 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -16,34 +16,6 @@
#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;
@@ -136,14 +108,15 @@
int max_slots = 214;
nid_t ino = le32_to_cpu(dir->footer.ino);
f2fs_hash_t namehash;
+ unsigned int dir_level = dir->i.i_dir_level;
int ret = 0;
namehash = f2fs_dentry_hash(de->name, de->len);
- nbucket = dir_buckets(level);
+ nbucket = dir_buckets(level, dir_level);
nblock = bucket_blocks(level);
- bidx = dir_block_index(level, 0, le32_to_cpu(namehash) % nbucket);
+ bidx = dir_block_index(level, dir_level, le32_to_cpu(namehash) % nbucket);
end_block = bidx + nblock;
dentry_blk = calloc(BLOCK_SZ, 1);
@@ -185,12 +158,6 @@
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))
@@ -199,7 +166,7 @@
return 0;
}
-static void f2fs_update_dentry(nid_t ino, umode_t mode,
+static void f2fs_update_dentry(nid_t ino, int file_type,
struct f2fs_dentry_ptr *d,
const unsigned char *name, int len, f2fs_hash_t name_hash,
unsigned int bit_pos)
@@ -214,7 +181,7 @@
memcpy(d->filename[bit_pos], name, len);
d->filename[bit_pos][len] = 0;
de->ino = cpu_to_le32(ino);
- set_de_type(de, mode);
+ de->file_type = file_type;
for (i = 0; i < slots; i++)
test_and_set_bit_le(bit_pos + i, d->bitmap);
}
@@ -223,23 +190,21 @@
* 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)
+ const unsigned char *name, int name_len, nid_t ino,
+ int file_type, block_t p_blkaddr, int inc_link)
{
int level = 0, current_depth, bit_pos;
int nbucket, nblock, bidx, block;
- 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);
+ unsigned int dir_level = parent->i.i_dir_level;
int ret;
- if (parent == NULL || child == NULL)
+ if (parent == NULL)
return -EINVAL;
if (!pino) {
@@ -262,9 +227,9 @@
if (level == current_depth)
++current_depth;
- nbucket = dir_buckets(level);
+ nbucket = dir_buckets(level, dir_level);
nblock = bucket_blocks(level);
- bidx = dir_block_index(level, 0, le32_to_cpu(dentry_hash) % nbucket);
+ bidx = dir_block_index(level, dir_level, le32_to_cpu(dentry_hash) % nbucket);
for (block = bidx; block <= (bidx + nblock - 1); block++) {
@@ -292,7 +257,7 @@
add_dentry:
make_dentry_ptr(&d, (void *)dentry_blk, 1);
- f2fs_update_dentry(ino, mode, &d, name, name_len, dentry_hash, bit_pos);
+ f2fs_update_dentry(ino, file_type, &d, name, name_len, dentry_hash, bit_pos);
ret = dev_write_block(dentry_blk, dn.data_blkaddr);
ASSERT(ret >= 0);
@@ -307,7 +272,7 @@
}
/* Update parent's i_links info*/
- if (S_ISDIR(mode)) {
+ if (inc_link && (file_type == F2FS_FT_DIR)){
u32 links = le32_to_cpu(parent->i.i_links);
parent->i.i_links = cpu_to_le32(links + 1);
dn.idirty = 1;
@@ -471,6 +436,102 @@
page_symlink(sbi, node_blk, de->link, size);
}
+int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
+ block_t p_blkaddr)
+{
+ struct f2fs_inode *inode = &(node->i);
+ unsigned int dir_level = node->i.i_dir_level;
+ nid_t ino = le32_to_cpu(node->footer.ino);
+ char inline_data[MAX_INLINE_DATA];
+ struct dnode_of_data dn = {0};
+ struct f2fs_dentry_ptr d;
+ unsigned long bit_pos = 0;
+ int ret = 0;
+
+ if (!(inode->i_inline & F2FS_INLINE_DENTRY))
+ return 0;
+
+ memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA);
+ memset(inline_data_addr(node), 0, MAX_INLINE_DATA);
+ inode->i_inline &= ~F2FS_INLINE_DENTRY;
+
+ ret = dev_write_block(node, p_blkaddr);
+ ASSERT(ret >= 0);
+
+ if (!dir_level) {
+ struct f2fs_inline_dentry *inline_dentry;
+ struct f2fs_dentry_block *dentry_blk;
+
+ dentry_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(dentry_blk);
+
+ set_new_dnode(&dn, node, NULL, ino);
+ get_dnode_of_data(sbi, &dn, 0, ALLOC_NODE);
+ if (dn.data_blkaddr == NULL_ADDR)
+ new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
+
+ inline_dentry = (struct f2fs_inline_dentry *)inline_data;
+ /* copy data from inline dentry block to new dentry block */
+ memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
+ INLINE_DENTRY_BITMAP_SIZE);
+ memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
+ SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
+
+ memcpy(dentry_blk->dentry, inline_dentry->dentry,
+ sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
+ memcpy(dentry_blk->filename, inline_dentry->filename,
+ NR_INLINE_DENTRY * F2FS_SLOT_LEN);
+
+ ret = dev_write_block(dentry_blk, dn.data_blkaddr);
+ ASSERT(ret >= 0);
+
+ MSG(1, "%s: copy inline entry to block\n", __func__);
+
+ free(dentry_blk);
+ return ret;
+ }
+
+ make_empty_dir(sbi, node);
+ make_dentry_ptr(&d, (void *)inline_data, 2);
+
+ while (bit_pos < d.max) {
+ struct f2fs_dir_entry *de;
+ const unsigned char *filename;
+ int namelen;
+
+ if (!test_bit_le(bit_pos, d.bitmap)) {
+ bit_pos++;
+ continue;
+ }
+
+ de = &d.dentry[bit_pos];
+ if (!de->name_len) {
+ bit_pos++;
+ continue;
+ }
+
+ filename = d.filename[bit_pos];
+ namelen = le32_to_cpu(de->name_len);
+
+ if (is_dot_dotdot(filename, namelen)) {
+ bit_pos += GET_DENTRY_SLOTS(namelen);
+ continue;
+ }
+
+ ret = f2fs_add_link(sbi, node, filename, namelen,
+ le32_to_cpu(de->ino),
+ de->file_type, p_blkaddr, 0);
+ if (ret)
+ MSG(0, "Convert file \"%s\" ERR=%d\n", filename, ret);
+ else
+ MSG(1, "%s: add inline entry to block\n", __func__);
+
+ bit_pos += GET_DENTRY_SLOTS(namelen);
+ }
+
+ return 0;
+}
+
int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
{
struct f2fs_node *parent, *child;
@@ -492,6 +553,13 @@
ret = dev_read_block(parent, ni.blk_addr);
ASSERT(ret >= 0);
+ /* Must convert inline dentry before the following opertions */
+ ret = convert_inline_dentry(sbi, parent, ni.blk_addr);
+ if (ret) {
+ MSG(0, "Convert inline dentry for pino=%x failed.\n", de->pino);
+ return -1;
+ }
+
ret = f2fs_find_entry(sbi, parent, de);
if (ret) {
MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
@@ -500,13 +568,6 @@
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);
@@ -515,7 +576,11 @@
init_inode_block(sbi, child, de);
- ret = f2fs_add_link(sbi, parent, child, ni.blk_addr);
+ ret = f2fs_add_link(sbi, parent, child->i.i_name,
+ le32_to_cpu(child->i.i_namelen),
+ le32_to_cpu(child->footer.ino),
+ map_de_type(le16_to_cpu(child->i.i_mode)),
+ ni.blk_addr, 1);
if (ret) {
MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
de->name, de->pino, ret);
diff --git a/fsck/dump.c b/fsck/dump.c
index 8cbeda1..22e2265 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -356,7 +356,7 @@
unsigned char name[F2FS_NAME_LEN + 1] = {0};
char path[1024] = {0};
char ans[255] = {0};
- int is_encrypt = file_is_encrypt(inode);
+ int enc_name = file_enc_name(inode);
int ret;
if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
@@ -377,7 +377,7 @@
/* make a file */
namelen = convert_encrypted_name(inode->i_name, namelen,
- name, is_encrypt);
+ name, enc_name);
name[namelen] = 0;
sprintf(path, "./lost_found/%s", name);
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 5c8eea5..efc43f6 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -444,9 +444,9 @@
[S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK,
};
-static inline void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
+static inline int map_de_type(umode_t mode)
{
- de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
+ return f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
}
static inline void *inline_xattr_addr(struct f2fs_inode *inode)
@@ -465,4 +465,41 @@
#define IS_SUM_NODE_SEG(footer) (footer.entry_type == SUM_TYPE_NODE)
#define IS_SUM_DATA_SEG(footer) (footer.entry_type == SUM_TYPE_DATA)
+static inline 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 inline unsigned int bucket_blocks(unsigned int level)
+{
+ if (level < MAX_DIR_HASH_DEPTH / 2)
+ return 2;
+ else
+ return 4;
+}
+
+static inline 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 inline int is_dot_dotdot(const unsigned char *name, const int len)
+{
+ if (len == 1 && name[0] == '.')
+ return 1;
+ if (len == 2 && name[0] == '.' && name[1] == '.')
+ return 1;
+ return 0;
+}
+
#endif /* _F2FS_H_ */
diff --git a/fsck/fsck.c b/fsck/fsck.c
index e97ee0a..56336ad 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -347,7 +347,7 @@
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, u8 *name)
+ struct node_info *ni)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
int ret;
@@ -470,7 +470,7 @@
/* Sanity check */
if (sanity_check_nid(sbi, x_nid, node_blk,
- F2FS_FT_XATTR, TYPE_XATTR, &ni, NULL)) {
+ F2FS_FT_XATTR, TYPE_XATTR, &ni)) {
ret = -EINVAL;
goto out;
}
@@ -484,7 +484,7 @@
}
int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
- u32 nid, u8 *name, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
+ u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
u32 *blk_cnt, struct child_info *child)
{
struct node_info ni;
@@ -493,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, name))
+ if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni))
goto err;
if (ntype == TYPE_INODE) {
@@ -720,7 +720,7 @@
blkaddr,
&child, (i_blocks == *blk_cnt),
ftype, nid, idx, ni->version,
- file_is_encrypt(&node_blk->i));
+ file_enc_name(&node_blk->i));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
} else if (c.fix_on) {
@@ -748,7 +748,7 @@
goto skip;
ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid,
- NULL, ftype, ntype, blk_cnt, &child);
+ ftype, ntype, blk_cnt, &child);
if (!ret) {
*blk_cnt = *blk_cnt + 1;
} else if (ret == -EINVAL) {
@@ -793,7 +793,7 @@
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, file_enc_name(&node_blk->i));
en[namelen] = '\0';
if (ftype == F2FS_FT_ORPHAN)
DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n",
@@ -878,7 +878,7 @@
blkaddr, child,
le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
nid, idx, ni->version,
- file_is_encrypt(inode));
+ file_enc_name(inode));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
} else if (c.fix_on) {
@@ -905,7 +905,7 @@
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
goto skip;
ret = fsck_chk_node_blk(sbi, inode,
- le32_to_cpu(node_blk->in.nid[i]), NULL,
+ le32_to_cpu(node_blk->in.nid[i]),
ftype, TYPE_DIRECT_NODE, blk_cnt, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
@@ -945,7 +945,7 @@
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
goto skip;
ret = fsck_chk_node_blk(sbi, inode,
- le32_to_cpu(node_blk->in.nid[i]), NULL,
+ le32_to_cpu(node_blk->in.nid[i]),
ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
@@ -1005,9 +1005,9 @@
}
int convert_encrypted_name(unsigned char *name, int len,
- unsigned char *new, int encrypted)
+ unsigned char *new, int enc_name)
{
- if (!encrypted) {
+ if (!enc_name) {
memcpy(new, name, len);
new[len] = 0;
return len;
@@ -1019,7 +1019,7 @@
static void print_dentry(__u32 depth, __u8 *name,
u8 *bitmap, struct f2fs_dir_entry *dentry,
- int max, int idx, int last_blk, int encrypted)
+ int max, int idx, int last_blk, int enc_name)
{
int last_de = 0;
int next_idx = 0;
@@ -1028,7 +1028,7 @@
int bit_offset;
unsigned char new[F2FS_NAME_LEN + 1];
- if (c.dbg_lv != -1)
+ if (!c.show_dentry)
return;
name_len = le16_to_cpu(dentry[idx].name_len);
@@ -1056,16 +1056,16 @@
for (i = 1; i < depth; i++)
printf("%c ", tree_mark[i]);
- convert_encrypted_name(name, name_len, new, encrypted);
+ convert_encrypted_name(name, name_len, new, enc_name);
printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
last_de ? '`' : '|',
new, le32_to_cpu(dentry[idx].ino),
- encrypted);
+ enc_name);
}
static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
- const unsigned char *name, u32 len, int encrypted)
+ const unsigned char *name, u32 len, int enc_name)
{
f2fs_hash_t hash_code = f2fs_dentry_hash(name, len);
@@ -1074,7 +1074,7 @@
unsigned char new[F2FS_NAME_LEN + 1];
convert_encrypted_name((unsigned char *)name, len,
- new, encrypted);
+ new, enc_name);
FIX_MSG("Mismatch hash_code for \"%s\" [%x:%x]",
new, le32_to_cpu(dentry->hash_code),
hash_code);
@@ -1084,33 +1084,6 @@
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)
{
@@ -1156,7 +1129,7 @@
struct child_info *child,
u8 *name, int len,
__u8 (*filename)[F2FS_SLOT_LEN],
- int encrypted)
+ int enc_name)
{
int fixed = 0;
@@ -1185,7 +1158,7 @@
}
}
- if (f2fs_check_hash_code(dentry, name, len, encrypted))
+ if (f2fs_check_hash_code(dentry, name, len, enc_name))
fixed = 1;
if (name[len] != '\0') {
@@ -1208,7 +1181,7 @@
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 encrypted)
+ int max, int last_blk, int enc_name)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
enum FILE_TYPE ftype;
@@ -1298,7 +1271,7 @@
name_len == 2)) {
ret = __chk_dots_dentries(sbi, &dentry[i],
child, name, name_len, &filenames[i],
- encrypted);
+ enc_name);
switch (ret) {
case 1:
fixed = 1;
@@ -1321,7 +1294,7 @@
}
}
- if (f2fs_check_hash_code(dentry + i, name, name_len, encrypted))
+ if (f2fs_check_hash_code(dentry + i, name, name_len, enc_name))
fixed = 1;
if (max == NR_DENTRY_IN_BLOCK) {
@@ -1340,7 +1313,7 @@
}
}
- en_len = convert_encrypted_name(name, name_len, en, encrypted);
+ en_len = convert_encrypted_name(name, name_len, en, enc_name);
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, en, name_len,
@@ -1348,11 +1321,11 @@
dentry[i].file_type);
print_dentry(fsck->dentry_depth, name, bitmap,
- dentry, max, i, last_blk, encrypted);
+ dentry, max, i, last_blk, enc_name);
blk_cnt = 1;
ret = fsck_chk_node_blk(sbi,
- NULL, le32_to_cpu(dentry[i].ino), name,
+ NULL, le32_to_cpu(dentry[i].ino),
ftype, TYPE_INODE, &blk_cnt, NULL);
if (ret && c.fix_on) {
@@ -1393,7 +1366,7 @@
de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
NR_INLINE_DENTRY, 1,
- file_is_encrypt(&node_blk->i));
+ file_enc_name(&node_blk->i));
if (dentries < 0) {
DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
fsck->dentry_depth);
@@ -1408,7 +1381,7 @@
}
int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
- struct child_info *child, int last_blk, int encrypted)
+ struct child_info *child, int last_blk, int enc_name)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct f2fs_dentry_block *de_blk;
@@ -1424,7 +1397,7 @@
dentries = __chk_dentries(sbi, child,
de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
- NR_DENTRY_IN_BLOCK, last_blk, encrypted);
+ NR_DENTRY_IN_BLOCK, last_blk, enc_name);
if (dentries < 0 && !c.ro) {
ret = dev_write_block(de_blk, blk_addr);
@@ -1445,7 +1418,7 @@
int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
struct child_info *child, int last_blk,
enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver,
- int encrypted)
+ int enc_name)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -1479,7 +1452,7 @@
if (ftype == F2FS_FT_DIR) {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA);
return fsck_chk_dentry_blk(sbi, blk_addr, child,
- last_blk, encrypted);
+ last_blk, enc_name);
} else {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA);
}
@@ -1528,7 +1501,7 @@
continue;
}
- ret = fsck_chk_node_blk(sbi, NULL, ino, NULL,
+ ret = fsck_chk_node_blk(sbi, NULL, ino,
F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
NULL);
if (!ret)
@@ -1711,7 +1684,7 @@
while (node) {
/* Sanity check */
if (sanity_check_nid(sbi, node->nid, node_blk,
- F2FS_FT_MAX, TYPE_INODE, &ni, NULL))
+ F2FS_FT_MAX, TYPE_INODE, &ni))
FIX_MSG("Failed to fix, rerun fsck.f2fs");
node_blk->i.i_links = cpu_to_le32(node->actual_links);
@@ -1779,9 +1752,11 @@
flags |= CP_ORPHAN_PRESENT_FLAG;
}
- set_cp(ckpt_flags, flags);
set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
+ flags = update_nat_bits_flags(sb, cp, flags);
+ set_cp(ckpt_flags, flags);
+
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);
@@ -1814,6 +1789,10 @@
ret = dev_write_block(cp, cp_blk_no++);
ASSERT(ret >= 0);
+
+ /* Write nat bits */
+ if (flags & CP_NAT_BITS_FLAG)
+ write_nat_bits(sbi, sb, cp, sbi->cur_cp);
}
int check_curseg_offset(struct f2fs_sb_info *sbi)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 5a6f018..c54eccb 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -119,7 +119,7 @@
extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
- u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
+ 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 *);
@@ -178,6 +178,11 @@
extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int,
struct f2fs_sit_block *);
+extern u32 update_nat_bits_flags(struct f2fs_super_block *,
+ struct f2fs_checkpoint *, u32);
+extern void write_nat_bits(struct f2fs_sb_info *, struct f2fs_super_block *,
+ struct f2fs_checkpoint *, int);
+
/* dump.c */
struct dump_option {
nid_t nid;
diff --git a/fsck/main.c b/fsck/main.c
index 6c94a70..c9411eb 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -29,7 +29,7 @@
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");
+ MSG(0, " -t show directory tree\n");
exit(1);
}
@@ -80,7 +80,7 @@
static int is_digits(char *optarg)
{
- int i;
+ unsigned int i;
for (i = 0; i < strlen(optarg); i++)
if (!isdigit(optarg[i]))
@@ -88,18 +88,20 @@
return i == strlen(optarg);
}
-static void error_out(void)
+static void error_out(char *prog)
{
- if (c.func == FSCK)
+ if (!strcmp("fsck.f2fs", prog))
fsck_usage();
- else if (c.func == DUMP)
+ else if (!strcmp("dump.f2fs", prog))
dump_usage();
- else if (c.func == DEFRAG)
+ else if (!strcmp("defrag.f2fs", prog))
defrag_usage();
- else if (c.func == RESIZE)
+ else if (!strcmp("resize.f2fs", prog))
resize_usage();
- else if (c.func == SLOAD)
+ else if (!strcmp("sload.f2fs", prog))
sload_usage();
+ else
+ MSG(0, "\nWrong progam.\n");
}
void f2fs_parse_options(int argc, char *argv[])
@@ -110,10 +112,8 @@
if (argc < 2) {
MSG(0, "\tError: Device not specified\n");
- error_out();
+ error_out(prog);
}
- c.devices[0].path = strdup(argv[argc - 1]);
- argv[argc-- - 1] = 0;
if (!strcmp("fsck.f2fs", prog)) {
const char *option_string = ":ad:fp:t";
@@ -164,7 +164,7 @@
MSG(0, "Info: Force to fix corruption\n");
break;
case 't':
- c.dbg_lv = -1;
+ c.show_dentry = 1;
break;
@@ -370,7 +370,14 @@
break;
}
}
- if (argc > optind) {
+
+ if (optind >= argc) {
+ MSG(0, "\tError: Device not specified\n");
+ error_out(prog);
+ }
+
+ c.devices[0].path = strdup(argv[optind]);
+ if (argc > (optind + 1)) {
c.dbg_lv = 0;
err = EUNKNOWN_ARG;
}
@@ -392,7 +399,7 @@
MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
break;
}
- error_out();
+ error_out(prog);
}
static void do_fsck(struct f2fs_sb_info *sbi)
@@ -440,7 +447,7 @@
/* Traverse all block recursively from root inode */
blk_cnt = 1;
- fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, (u8 *)"/",
+ fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
fsck_verify(sbi);
fsck_free(sbi);
@@ -535,8 +542,9 @@
return -1;
}
- if (c.target_sectors <=
- (get_sb(block_count) << get_sb(log_sectors_per_block))) {
+ /* may different sector size */
+ if ((c.target_sectors * c.sector_size >>
+ get_sb(log_blocksize)) <= get_sb(block_count)) {
ASSERT_MSG("Nothing to resize, now only support resize to expand\n");
return -1;
}
@@ -566,6 +574,8 @@
f2fs_parse_options(argc, argv);
if (f2fs_devs_are_umounted() < 0) {
+ if (errno == EBUSY)
+ return -1;
if (!c.ro || c.func == DEFRAG) {
MSG(0, "\tError: Not available on mounted device!\n");
return -1;
diff --git a/fsck/mount.c b/fsck/mount.c
index b8e8c45..a0b0bea 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -40,14 +40,14 @@
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);
+ int enc_name = file_enc_name(inode);
- namelen = convert_encrypted_name(inode->i_name, namelen, en, is_encrypt);
+ namelen = convert_encrypted_name(inode->i_name, namelen, en, enc_name);
en[namelen] = '\0';
if (name && namelen) {
inode->i_name[namelen] = '\0';
MSG(0, " - File name : %s%s\n", en,
- is_encrypt ? " <encrypted>" : "");
+ enc_name ? " <encrypted>" : "");
setlocale(LC_ALL, "");
MSG(0, " - File size : %'llu (bytes)\n",
le64_to_cpu(inode->i_size));
@@ -257,6 +257,10 @@
MSG(0, "%s", " orphan_inodes");
if (flag & CP_FASTBOOT_FLAG)
MSG(0, "%s", " fastboot");
+ if (flag & CP_NAT_BITS_FLAG)
+ MSG(0, "%s", " nat_bits");
+ if (flag & CP_TRIMMED_FLAG)
+ MSG(0, "%s", " trimmed");
if (flag & CP_UMOUNT_FLAG)
MSG(0, "%s", " unmount");
else
@@ -545,7 +549,7 @@
cp = (struct f2fs_checkpoint *)cp_page_1;
crc_offset = get_cp(checksum_offset);
- if (crc_offset >= blk_size)
+ if (crc_offset > (blk_size - sizeof(__le32)))
goto invalid_cp1;
crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
@@ -563,7 +567,7 @@
cp = (struct f2fs_checkpoint *)cp_page_2;
crc_offset = get_cp(checksum_offset);
- if (crc_offset >= blk_size)
+ if (crc_offset > (blk_size - sizeof(__le32)))
goto invalid_cp2;
crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset));
@@ -754,6 +758,92 @@
return 0;
}
+u32 update_nat_bits_flags(struct f2fs_super_block *sb,
+ struct f2fs_checkpoint *cp, u32 flags)
+{
+ u_int32_t nat_bits_bytes, nat_bits_blocks;
+
+ nat_bits_bytes = get_sb(segment_count_nat) << 5;
+ nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+ F2FS_BLKSIZE - 1);
+ if (get_cp(cp_pack_total_block_count) <=
+ (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks)
+ flags |= CP_NAT_BITS_FLAG;
+ else
+ flags &= (~CP_NAT_BITS_FLAG);
+
+ return flags;
+}
+
+/* should call flush_journal_entries() bfore this */
+void write_nat_bits(struct f2fs_sb_info *sbi,
+ struct f2fs_super_block *sb, struct f2fs_checkpoint *cp, int set)
+{
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ u_int32_t nat_blocks = get_sb(segment_count_nat) <<
+ (get_sb(log_blocks_per_seg) - 1);
+ u_int32_t nat_bits_bytes = nat_blocks >> 3;
+ u_int32_t nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) +
+ 8 + F2FS_BLKSIZE - 1);
+ unsigned char *nat_bits, *full_nat_bits, *empty_nat_bits;
+ struct f2fs_nat_block *nat_block;
+ u_int32_t i, j;
+ block_t blkaddr;
+ int ret;
+
+ nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks);
+ ASSERT(nat_bits);
+
+ nat_block = malloc(F2FS_BLKSIZE);
+ ASSERT(nat_block);
+
+ full_nat_bits = nat_bits + 8;
+ empty_nat_bits = full_nat_bits + nat_bits_bytes;
+
+ memset(full_nat_bits, 0, nat_bits_bytes);
+ memset(empty_nat_bits, 0, nat_bits_bytes);
+
+ for (i = 0; i < nat_blocks; i++) {
+ int seg_off = i >> get_sb(log_blocks_per_seg);
+ int valid = 0;
+
+ blkaddr = (pgoff_t)(get_sb(nat_blkaddr) +
+ (seg_off << get_sb(log_blocks_per_seg) << 1) +
+ (i & ((1 << get_sb(log_blocks_per_seg)) - 1)));
+
+ if (f2fs_test_bit(i, nm_i->nat_bitmap))
+ blkaddr += (1 << get_sb(log_blocks_per_seg));
+
+ ret = dev_read_block(nat_block, blkaddr);
+ ASSERT(ret >= 0);
+
+ for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) {
+ if ((i == 0 && j == 0) ||
+ nat_block->entries[j].block_addr != NULL_ADDR)
+ valid++;
+ }
+ if (valid == 0)
+ test_and_set_bit_le(i, empty_nat_bits);
+ else if (valid == NAT_ENTRY_PER_BLOCK)
+ test_and_set_bit_le(i, full_nat_bits);
+ }
+ *(__le64 *)nat_bits = get_cp_crc(cp);
+ free(nat_block);
+
+ blkaddr = get_sb(segment0_blkaddr) + (set <<
+ get_sb(log_blocks_per_seg)) - nat_bits_blocks;
+
+ DBG(1, "\tWriting NAT bits pages, at offset 0x%08x\n", blkaddr);
+
+ for (i = 0; i < nat_bits_blocks; i++) {
+ if (dev_write_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i))
+ ASSERT_MSG("\tError: write NAT bits to disk!!!\n");
+ }
+ MSG(0, "Info: Write valid nat_bits in checkpoint\n");
+
+ free(nat_bits);
+}
+
int init_node_manager(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
@@ -997,11 +1087,8 @@
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);
- }
+ ret = dev_write_block(sum_blk, GET_SUM_BLKADDR(sbi, segno));
+ ASSERT(ret >= 0);
if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
type == SEG_TYPE_MAX)
@@ -1794,12 +1881,13 @@
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));
+ flags = update_nat_bits_flags(sb, cp, flags);
+ set_cp(ckpt_flags, flags);
+
crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
*((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
@@ -1833,6 +1921,10 @@
/* write the last cp */
ret = dev_write_block(cp, cp_blk_no++);
ASSERT(ret >= 0);
+
+ /* Write nat bits */
+ if (flags & CP_NAT_BITS_FLAG)
+ write_nat_bits(sbi, sb, cp, sbi->cur_cp);
}
void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
@@ -2008,6 +2100,7 @@
int f2fs_do_mount(struct f2fs_sb_info *sbi)
{
struct f2fs_checkpoint *cp = NULL;
+ struct f2fs_super_block *sb = NULL;
int ret;
sbi->active_logs = NR_CURSEG_TYPE;
@@ -2017,12 +2110,13 @@
if (ret)
return -1;
}
+ sb = F2FS_RAW_SUPER(sbi);
- ret = check_sector_size(sbi->raw_super);
+ ret = check_sector_size(sb);
if (ret)
return -1;
- print_raw_sb_info(F2FS_RAW_SUPER(sbi));
+ print_raw_sb_info(sb);
init_sb_info(sbi);
@@ -2068,6 +2162,30 @@
return -1;
}
+ /* Check nat_bits */
+ if (is_set_ckpt_flags(cp, CP_NAT_BITS_FLAG)) {
+ u_int32_t nat_bits_bytes, nat_bits_blocks;
+ __le64 *kaddr;
+ u_int32_t blk;
+
+ blk = get_sb(cp_blkaddr) + (1 << get_sb(log_blocks_per_seg));
+ if (sbi->cur_cp == 2)
+ blk += 1 << get_sb(log_blocks_per_seg);
+
+ nat_bits_bytes = get_sb(segment_count_nat) << 5;
+ nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+ F2FS_BLKSIZE - 1);
+ blk -= nat_bits_blocks;
+
+ kaddr = malloc(PAGE_SIZE);
+ ret = dev_read_block(kaddr, blk);
+ ASSERT(ret >= 0);
+ if (*kaddr != get_cp_crc(cp))
+ write_nat_bits(sbi, sb, cp, sbi->cur_cp);
+ else
+ MSG(0, "Info: Found valid nat_bits in checkpoint\n");
+ free(kaddr);
+ }
return 0;
}
diff --git a/fsck/resize.c b/fsck/resize.c
index 29cb1c0..4584d6f 100644
--- a/fsck/resize.c
+++ b/fsck/resize.c
@@ -415,6 +415,7 @@
struct f2fs_super_block *new_sb, unsigned int offset)
{
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ unsigned long long cp_ver = get_cp(checkpoint_ver);
struct f2fs_checkpoint *new_cp;
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
unsigned int free_segment_count, new_segment_count;
@@ -422,6 +423,7 @@
block_t orphan_blks = 0;
block_t new_cp_blk_no, old_cp_blk_no;
u_int32_t crc = 0;
+ u32 flags;
void *buf;
int i, ret;
@@ -474,11 +476,17 @@
((get_newsb(segment_count_nat) / 2) <<
get_newsb(log_blocks_per_seg)) / 8);
+ /* update nat_bits flag */
+ flags = update_nat_bits_flags(new_sb, cp, get_cp(ckpt_flags));
+ set_cp(ckpt_flags, flags);
+
memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
(unsigned char *)cp);
+ new_cp->checkpoint_ver = cpu_to_le64(cp_ver + 1);
crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
- *((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+ *((__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);
@@ -519,6 +527,10 @@
ret = dev_write_block(new_cp, new_cp_blk_no++);
ASSERT(ret >= 0);
+ /* Write nat bits */
+ if (flags & CP_NAT_BITS_FLAG)
+ write_nat_bits(sbi, new_sb, new_cp, sbi->cur_cp == 1 ? 2 : 1);
+
/* disable old checkpoint */
memset(buf, 0, BLOCK_SZ);
ret = dev_write_block(buf, old_cp_blk_no);
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 97ee297..dd2635b 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -256,6 +256,7 @@
struct f2fs_configuration {
u_int32_t reserved_segments;
u_int32_t new_reserved_segments;
+ int sparse_mode;
int zoned_mode;
int zoned_model;
size_t zone_blocks;
@@ -268,6 +269,7 @@
u_int32_t start_sector;
u_int32_t total_segments;
u_int32_t sector_size;
+ u_int64_t device_size;
u_int64_t total_sectors;
u_int64_t wanted_total_sectors;
u_int64_t target_sectors;
@@ -285,7 +287,9 @@
char *extension_list;
const char *rootdev_name;
int dbg_lv;
+ int show_dentry;
int trim;
+ int trimmed;
int func;
void *private;
int fix_on;
@@ -304,7 +308,7 @@
/* sload parameters */
char *from_dir;
char *mount_point;
-} __attribute__((packed));
+};
#ifdef CONFIG_64BIT
#define BITS_PER_LONG 64
@@ -514,6 +518,9 @@
/*
* For checkpoint
*/
+#define CP_TRIMMED_FLAG 0x00000100
+#define CP_NAT_BITS_FLAG 0x00000080
+#define CP_CRC_RECOVERY_FLAG 0x00000040
#define CP_FASTBOOT_FLAG 0x00000020
#define CP_FSCK_FLAG 0x00000010
#define CP_ERROR_FLAG 0x00000008
@@ -610,8 +617,10 @@
#define FADVISE_COLD_BIT 0x01
#define FADVISE_LOST_PINO_BIT 0x02
#define FADVISE_ENCRYPT_BIT 0x04
+#define FADVISE_ENC_NAME_BIT 0x08
#define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
+#define file_enc_name(fi) ((fi)->i_advise & FADVISE_ENC_NAME_BIT)
struct f2fs_inode {
__le16 i_mode; /* file mode */
@@ -1083,4 +1092,15 @@
return max_ovp;
}
+static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp)
+{
+ u_int64_t cp_ver = get_cp(checkpoint_ver);
+ size_t crc_offset = get_cp(checksum_offset);
+ u_int32_t crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp +
+ crc_offset));
+
+ cp_ver |= ((u_int64_t)crc << 32);
+ return cpu_to_le64(cp_ver);
+}
+
#endif /*__F2FS_FS_H */
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 93d3da9..31836db 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -32,6 +32,10 @@
#define MODELINQUIRY 0x12,0x00,0x00,0x00,0x4A,0x00
#endif
+#ifndef _WIN32 /* O_BINARY is windows-specific flag */
+#define O_BINARY 0
+#endif
+
/*
* UTF conversion codes are Copied from exfat tools.
*/
@@ -568,9 +572,10 @@
c.segs_per_sec = 1;
c.secs_per_zone = 1;
c.segs_per_zone = 1;
- c.heap = 1;
+ c.heap = 0;
c.vol_label = "";
c.trim = 1;
+ c.trimmed = 0;
c.ro = 0;
c.kd = -1;
}
@@ -688,7 +693,11 @@
#endif
struct device_info *dev = c.devices + i;
- fd = open((char *)dev->path, O_RDWR);
+ if (c.sparse_mode) {
+ fd = open((char *)dev->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ } else {
+ fd = open((char *)dev->path, O_RDWR);
+ }
if (fd < 0) {
MSG(0, "\tError: Failed to open the device!\n");
return -1;
@@ -709,7 +718,9 @@
return -1;
}
- if (S_ISREG(stat_buf.st_mode)) {
+ if (c.sparse_mode) {
+ dev->total_sectors = c.device_size / dev->sector_size;
+ } else if (S_ISREG(stat_buf.st_mode)) {
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)
@@ -730,10 +741,12 @@
#endif
dev->total_sectors /= dev->sector_size;
- if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
- c.start_sector = 0;
- else
- c.start_sector = geom.start;
+ if (i == 0) {
+ if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
+ c.start_sector = 0;
+ else
+ c.start_sector = geom.start;
+ }
#ifndef WITH_ANDROID
/* Send INQUIRY command */
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index c09db36..aa99068 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -25,6 +25,19 @@
struct f2fs_configuration c;
+#ifdef WITH_ANDROID
+#include <sparse/sparse.h>
+struct sparse_file *f2fs_sparse_file;
+
+struct buf_item {
+ void *buf;
+ size_t len;
+ struct buf_item *next;
+};
+
+struct buf_item *buf_list;
+#endif
+
static int __get_device_fd(__u64 *offset)
{
__u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
@@ -46,6 +59,8 @@
*/
int dev_read_version(void *buf, __u64 offset, size_t len)
{
+ if (c.sparse_mode)
+ return 0;
if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
return -1;
if (read(c.kd, buf, len) < 0)
@@ -55,8 +70,12 @@
int dev_read(void *buf, __u64 offset, size_t len)
{
- int fd = __get_device_fd(&offset);
+ int fd;
+ if (c.sparse_mode)
+ return 0;
+
+ fd = __get_device_fd(&offset);
if (fd < 0)
return fd;
@@ -80,10 +99,40 @@
#endif
}
+#ifdef WITH_ANDROID
+static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len)
+{
+ struct buf_item *bi = calloc(1, sizeof(struct buf_item));
+
+ if (bi == NULL) {
+ return -1;
+ }
+ bi->buf = malloc(byte_len);
+ if (bi->buf == NULL) {
+ free(bi);
+ return -1;
+ }
+
+ bi->len = byte_len;
+ memcpy(bi->buf, buf, byte_len);
+ bi->next = buf_list;
+ buf_list = bi;
+
+ sparse_file_add_data(f2fs_sparse_file, bi->buf, byte_len, byte_offset/F2FS_BLKSIZE);
+ return 0;
+}
+#else
+static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len) { return 0; }
+#endif
+
int dev_write(void *buf, __u64 offset, size_t len)
{
- int fd = __get_device_fd(&offset);
+ int fd;
+ if (c.sparse_mode)
+ return dev_write_sparse(buf, offset, len);
+
+ fd = __get_device_fd(&offset);
if (fd < 0)
return fd;
@@ -110,8 +159,12 @@
int dev_fill(void *buf, __u64 offset, size_t len)
{
- int fd = __get_device_fd(&offset);
+ int fd;
+ if (c.sparse_mode)
+ return 0;
+
+ fd = __get_device_fd(&offset);
if (fd < 0)
return fd;
@@ -144,6 +197,20 @@
{
int i;
+#ifdef WITH_ANDROID
+ if (c.sparse_mode) {
+ sparse_file_write(f2fs_sparse_file, c.devices[0].fd, /*gzip*/0, /*sparse*/1, /*crc*/0);
+ sparse_file_destroy(f2fs_sparse_file);
+ while (buf_list) {
+ struct buf_item *bi = buf_list;
+ buf_list = buf_list->next;
+ free(bi->buf);
+ free(bi);
+ }
+ f2fs_sparse_file = NULL;
+ }
+#endif
+
/*
* We should call fsync() to flush out all the dirty pages
* in the block device page cache.
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index bdb9755..c2f9c86 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -16,29 +16,46 @@
.I device
]
[
+.B \-d
+.I debugging-level
+]
+[
+.B \-e
+.I extension-list
+]
+[
+.B \-f
+]
+[
.B \-l
.I volume-label
]
[
+.B \-m
+]
+[
.B \-o
.I overprovision-ratio-percentage
]
[
+.B \-O
+.I feature-list
+]
+[
+.B \-q
+]
+[
.B \-s
.I #-of-segments-per-section
]
[
+.B \-t
+.I nodiscard/discard
+]
+[
.B \-z
.I #-of-sections-per-zone
]
-[
-.B \-e
-.I extenstion-list
-]
-[
-.B \-d
-.I debugging-level
-]
.I device
.I [sectors]
.SH DESCRIPTION
@@ -63,23 +80,9 @@
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. If not specified, the best
-number will be assigned automatically accoring to the partition size.
-.TP
-.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 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.
-F2FS allocates segments for active logs with separated zones as much as possible.
-The default number is 1, which means a zone consists of one section.
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
.TP
.BI \-e " extension-list"
Specify a file extension list in order f2fs to treat them as cold files.
@@ -87,9 +90,45 @@
The default list includes most of multimedia file extensions such as jpg, gif,
mpeg, mkv, and so on.
.TP
-.BI \-d " debug-level"
-Specify the level of debugging options.
-The default number is 0, which shows basic debugging messages.
+.BI \-f
+Force overwrite when an existing filesystem is detected on the device.
+By default, mkfs.f2fs will not write to the device if it suspects that
+there is a filesystem or partition table on the device already.
+.TP
+.BI \-l " volume-label"
+Specify the volume label to the partition mounted as F2FS.
+.TP
+.BI \-m
+Specify f2fs filesystem to supports the block zoned feature.
+Without it, the filesystem isn't supports the feature.
+.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. If not specified, the best
+number will be assigned automatically accoring to the partition size.
+.TP
+.BI \-O " feature-list"
+Specify a feature list in order f2fs filesystem will supports.
+e.g "encrypt" and so on.
+.TP
+.BI \-q
+Quiet mode.
+With it, mkfs.f2fs does not show any messages include the basic messages.
+.TP
+.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 1, which means one segment is assigned to a section.
+.TP
+.BI \-t " nodiscard/discard"
+Specify 1 or 0 to enable/disable discard policy.
+If the value is equal to 1, discard policy is enabled, otherwise is disable.
+The default value is 1.
+.TP
+.BI \-z " #-of-sections-per-zone"
+Specify the number of sections per zone. A zone consists of multiple sections.
+F2FS allocates segments for active logs with separated zones as much as possible.
+The default number is 1, which means a zone consists of one section.
.TP
.SH AUTHOR
This version of
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index 8b4c16c..0ea8b49 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -1,14 +1,14 @@
## Makefile.am
-AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
+AM_CPPFLAGS = ${libuuid_CFLAGS} ${libblkid_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 f2fs_format_utils.h $(top_srcdir)/include/f2fs_fs.h
-mkfs_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+mkfs_f2fs_LDADD = ${libuuid_LIBS} ${libblkid_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 \
+libf2fs_format_la_LDFLAGS = -luuid -L$(top_builddir)/lib -lf2fs \
-version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE)
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 3c13026..ff1153a 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -117,6 +117,24 @@
free(c.extension_list);
}
+static void verify_cur_segs(void)
+{
+ int i, j;
+
+ for (i = 0; i < NR_CURSEG_TYPE; i++) {
+ for (j = 0; j < NR_CURSEG_TYPE; j++)
+ if (c.cur_seg[i] == c.cur_seg[j])
+ break;
+ }
+
+ if (i == NR_CURSEG_TYPE && j == NR_CURSEG_TYPE)
+ return;
+
+ c.cur_seg[0] = 0;
+ for (i = 1; i < NR_CURSEG_TYPE; i++)
+ c.cur_seg[i] = next_zone(i - 1);
+}
+
static int f2fs_prepare_super_block(void)
{
u_int32_t blk_size_bytes;
@@ -337,10 +355,6 @@
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) ||
@@ -349,6 +363,10 @@
return -1;
}
+ c.reserved_segments =
+ (2 * (100 / c.overprovision + 1) + 6)
+ * c.segs_per_sec;
+
uuid_generate(sb->uuid);
utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
@@ -376,10 +394,17 @@
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);
+ c.cur_seg[CURSEG_COLD_DATA] =
+ max(last_zone((total_zones >> 2)),
+ next_zone(CURSEG_COLD_NODE));
+ c.cur_seg[CURSEG_WARM_DATA] =
+ max(last_zone((total_zones >> 1)),
+ next_zone(CURSEG_COLD_DATA));
}
+ /* if there is redundancy, reassign it */
+ verify_cur_segs();
+
cure_extension_list();
/* get kernel version */
@@ -472,8 +497,10 @@
struct f2fs_summary_block *sum = NULL;
struct f2fs_journal *journal;
u_int32_t blk_size_bytes;
+ u_int32_t nat_bits_bytes, nat_bits_blocks;
+ unsigned char *nat_bits = NULL, *empty_nat_bits;
u_int64_t cp_seg_blk = 0;
- u_int32_t crc = 0;
+ u_int32_t crc = 0, flags;
unsigned int i;
char *cp_payload = NULL;
char *sum_compact, *sum_compact_p;
@@ -499,10 +526,19 @@
}
sum_compact_p = sum_compact;
+ nat_bits_bytes = get_sb(segment_count_nat) << 5;
+ nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+ F2FS_BLKSIZE - 1);
+ nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks);
+ if (nat_bits == NULL) {
+ MSG(1, "\tError: Calloc Failed for nat bits buffer!!!\n");
+ goto free_sum_compact;
+ }
+
cp_payload = calloc(F2FS_BLKSIZE, 1);
if (cp_payload == NULL) {
MSG(1, "\tError: Calloc Failed for cp_payload!!!\n");
- goto free_sum_compact;
+ goto free_nat_bits;
}
/* 1. cp page 1 of checkpoint pack 1 */
@@ -539,7 +575,15 @@
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);
+ flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG;
+ if (get_cp(cp_pack_total_block_count) <=
+ (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks)
+ flags |= CP_NAT_BITS_FLAG;
+
+ if (c.trimmed)
+ flags |= CP_TRIMMED_FLAG;
+
+ set_cp(ckpt_flags, flags);
set_cp(cp_pack_start_sum, 1 + get_sb(cp_payload));
set_cp(valid_node_count, 1);
set_cp(valid_inode_count, 1);
@@ -702,6 +746,31 @@
goto free_cp_payload;
}
+ /* write NAT bits, if possible */
+ if (flags & CP_NAT_BITS_FLAG) {
+ uint32_t i;
+
+ *(__le64 *)nat_bits = get_cp_crc(cp);
+ empty_nat_bits = nat_bits + 8 + nat_bits_bytes;
+ memset(empty_nat_bits, 0xff, nat_bits_bytes);
+ test_and_clear_bit_le(0, empty_nat_bits);
+
+ /* write the last blocks in cp pack */
+ cp_seg_blk = get_sb(segment0_blkaddr) + (1 <<
+ get_sb(log_blocks_per_seg)) - nat_bits_blocks;
+
+ DBG(1, "\tWriting NAT bits pages, at offset 0x%08"PRIx64"\n",
+ cp_seg_blk);
+
+ for (i = 0; i < nat_bits_blocks; i++) {
+ if (dev_write_block(nat_bits + i *
+ F2FS_BLKSIZE, cp_seg_blk + i)) {
+ MSG(1, "\tError: write NAT bits to disk!!!\n");
+ goto free_cp_payload;
+ }
+ }
+ }
+
/* cp page 1 of check point pack 2
* Initiatialize other checkpoint pack with version zero
*/
@@ -741,6 +810,8 @@
free_cp_payload:
free(cp_payload);
+free_nat_bits:
+ free(nat_bits);
free_sum_compact:
free(sum_compact);
free_sum:
@@ -775,6 +846,12 @@
#ifndef WITH_ANDROID
static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
{
+ u_int64_t next_blkaddr = 0;
+ u_int64_t root_inode_pos = get_sb(main_blkaddr);
+
+ /* only root inode was written before truncating dnodes */
+ root_inode_pos += c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg;
+
if (c.zoned_mode)
return 0;
do {
@@ -787,6 +864,7 @@
return -1;
}
+ next_blkaddr = le32_to_cpu(raw_node->footer.next_blkaddr);
memset(raw_node, 0, F2FS_BLKSIZE);
DBG(1, "\tDiscard dnode, at offset 0x%08"PRIx64"\n", offset);
@@ -794,7 +872,10 @@
MSG(1, "\tError: While discarding direct node!!!\n");
return -1;
}
- offset = le32_to_cpu(raw_node->footer.next_blkaddr);
+ offset = next_blkaddr;
+ /* should avoid recursive chain due to stale data */
+ if (offset == root_inode_pos)
+ break;
} while (1);
return 0;
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index 5bb1faf..5525d1c 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -17,28 +17,42 @@
#include <sys/mount.h>
#include <time.h>
#include <uuid/uuid.h>
+#include <errno.h>
+
+#include "config.h"
+#ifdef HAVE_LIBBLKID
+# include <blkid/blkid.h>
+#endif
#include "f2fs_fs.h"
#include "f2fs_format_utils.h"
+#ifdef WITH_ANDROID
+#include <sparse/sparse.h>
+extern struct sparse_file *f2fs_sparse_file;
+#endif
+
extern struct f2fs_configuration c;
+static int force_overwrite = 0;
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, " -a heap-based allocation [default:0]\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, " -f force overwrite the exist filesystem\n");
MSG(0, " -l label\n");
+ MSG(0, " -m support zoned block device [default:0]\n");
MSG(0, " -o overprovision ratio [default:5]\n");
- MSG(0, " -O set feature\n");
+ MSG(0, " -O [feature list] e.g. \"encrypt\"\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, " -S sparse mode\n");
MSG(0, " -t 0: nodiscard, 1: discard [default:1]\n");
- MSG(0, " -m support zoned block device [default:0]\n");
+ MSG(0, " -z # of sections per zone [default:1]\n");
MSG(0, "sectors: number of sectors. [default: determined by device size]\n");
exit(1);
}
@@ -62,6 +76,8 @@
static void parse_feature(const char *features)
{
+ while (*features == ' ')
+ features++;
if (!strcmp(features, "encrypt")) {
c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
} else {
@@ -72,7 +88,7 @@
static void f2fs_parse_options(int argc, char *argv[])
{
- static const char *option_string = "qa:c:d:e:l:mo:O:s:z:t:";
+ static const char *option_string = "qa:c:d:e:l:mo:O:s:S:z:t:f";
int32_t option=0;
while ((option = getopt(argc,argv,option_string)) != EOF) {
@@ -122,12 +138,20 @@
case 's':
c.segs_per_sec = atoi(optarg);
break;
+ case 'S':
+ c.device_size = atoll(optarg);
+ c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
+ c.sparse_mode = 1;
+ break;
case 'z':
c.secs_per_zone = atoi(optarg);
break;
case 't':
c.trim = atoi(optarg);
break;
+ case 'f':
+ force_overwrite = 1;
+ break;
default:
MSG(0, "\tError: Unknown option %c\n",option);
mkfs_usage();
@@ -151,10 +175,86 @@
c.wanted_total_sectors = atoll(argv[optind+1]);
}
+ if (c.sparse_mode)
+ c.trim = 0;
+
if (c.zoned_mode)
c.feature |= cpu_to_le32(F2FS_FEATURE_BLKZONED);
}
+#ifdef HAVE_LIBBLKID
+static int f2fs_dev_is_overwrite(const char *device)
+{
+ const char *type;
+ blkid_probe pr = NULL;
+ int ret = -1;
+
+ if (!device || !*device)
+ return 0;
+
+ pr = blkid_new_probe_from_filename(device);
+ if (!pr)
+ goto out;
+
+ ret = blkid_probe_enable_partitions(pr, 1);
+ if (ret < 0)
+ goto out;
+
+ ret = blkid_do_fullprobe(pr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Blkid returns 1 for nothing found and 0 when it finds a signature,
+ * but we want the exact opposite, so reverse the return value here.
+ *
+ * In addition print some useful diagnostics about what actually is
+ * on the device.
+ */
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
+ MSG(0, "\t%s appears to contain an existing filesystem (%s).\n",
+ device, type);
+ } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
+ MSG(0, "\t%s appears to contain a partition table (%s).\n",
+ device, type);
+ } else {
+ MSG(0, "\t%s appears to contain something weird according to blkid\n",
+ device);
+ }
+ ret = 1;
+out:
+ if (pr)
+ blkid_free_probe(pr);
+ if (ret == -1)
+ MSG(0, "\tprobe of %s failed, cannot detect existing filesystem.\n",
+ device);
+ return ret;
+}
+
+static int f2fs_check_overwrite(void)
+{
+ int i;
+
+ for (i = 0; i < c.ndevs; i++)
+ if (f2fs_dev_is_overwrite((char *)c.devices[i].path))
+ return -1;
+ return 0;
+}
+
+#else
+
+static int f2fs_check_overwrite(void)
+{
+ return 0;
+}
+
+#endif /* HAVE_LIBBLKID */
+
int main(int argc, char *argv[])
{
f2fs_init_configuration();
@@ -163,8 +263,14 @@
f2fs_show_info();
+ if (!force_overwrite && f2fs_check_overwrite()) {
+ MSG(0, "\tUse the -f option to force overwrite.\n");
+ return -1;
+ }
+
if (f2fs_devs_are_umounted() < 0) {
- MSG(0, "\tError: Not available on mounted device!\n");
+ if (errno != EBUSY)
+ MSG(0, "\tError: Not available on mounted device!\n");
return -1;
}
@@ -185,6 +291,17 @@
return -1;
}
+ if (c.sparse_mode) {
+#ifndef WITH_ANDROID
+ MSG(0, "\tError: Sparse mode is only supported for android\n");
+ return -1;
+#else
+ if (f2fs_sparse_file)
+ sparse_file_destroy(f2fs_sparse_file);
+ f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
+#endif
+ }
+
if (f2fs_format_device() < 0)
return -1;
diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c
index fc80ec6..558684d 100644
--- a/mkfs/f2fs_format_utils.c
+++ b/mkfs/f2fs_format_utils.c
@@ -90,5 +90,6 @@
for (i = 0; i < c.ndevs; i++)
if (trim_device(i))
return -1;
+ c.trimmed = 1;
return 0;
}
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 69a0bb1..5a9303f 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,7 +2,10 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
-sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs
+sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs f2fscrypt
f2fstat_SOURCES = f2fstat.c
fibmap_f2fs_SOURCES = fibmap.c
parse_f2fs_SOURCES = f2fs_io_parse.c
+f2fscrypt_SOURCES = f2fscrypt.c sha512.c
+f2fscrypt_LDFLAGS = -luuid
+dist_man_MANS = f2fscrypt.8
diff --git a/tools/f2fscrypt.8 b/tools/f2fscrypt.8
new file mode 100644
index 0000000..a60adc8
--- /dev/null
+++ b/tools/f2fscrypt.8
@@ -0,0 +1,102 @@
+.TH F2FSCRYPT 8
+.SH NAME
+f2fscrypt \- f2fs filesystem encryption utility
+.SH SYNOPSIS
+.B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ]
+.br
+.B f2fscrypt new_session
+.br
+.B f2fscrypt get_policy \fIpath\fR ...
+.br
+.B f2fscrypt set_policy \fIpolicy path\fR ...
+.SH DESCRIPTION
+.B f2fscrypt
+performs encryption management for f2fs file systems.
+.SH COMMANDS
+.TP
+.B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ]
+Prompts the user for a passphrase and inserts it into the specified
+keyring. If no keyring is specified, f2fscrypt will use the session
+keyring if it exists or the user session keyring if it does not.
+.IP
+If one or more directory paths are specified, f2fscrypt will try to
+set the policy of those directories to use the key just entered by
+the user.
+.TP
+.B f2fscrypt get_policy \fIpath\fR ...
+Print the policy for the directories specified on the command line.
+.TP
+.B f2fscrypt new_session
+Give the invoking process (typically a shell) a new session keyring,
+discarding its old session keyring.
+.TP
+.B f2fscrypt set_policy \fIpolicy path\fR ...
+Sets the policy for the directories specified on the command line.
+All directories must be empty to set the policy; if the directory
+already has a policy established, f2fscrypt will validate that the
+policy matches what was specified. A policy is an encryption key
+identifier consisting of 16 hexadecimal characters.
+.SH NOTES
+The target directory must be empty.
+.SH EXAMPLE
+.nf
+Formats a f2fs filesytem that supports encrypt.
+
+.ft R
+# mkfs.f2fs -O encrypt /dev/sdxx
+# mount /dev/sdxx /encrypted/
+# mkdir /encrypted/dir
+
+.nf
+First create the key in the keyring use an simple salt
+(or generate a random salt).
+Then use it to set the policy for the directory to be encrypted.
+
+.ft R
+# f2fscrypt add_key -S 0x1234
+ Enter passphrase (echo disabled):
+ Added key with descriptor [28e21cc0c4393da1]
+
+# f2fscrypt set_policy 28e21cc0c4393da1 /encrypted/dir
+ Key with descriptor [28e21cc0c4393da1] applied to /encrypted/dir.
+
+# touch /encrypted/dir/test.txt
+# ls -l /encrypted/dir/
+ -rw-r--r--. 1 root root 0 Mar 5 21:41 test.txt
+
+.nf
+After each reboot, the same command can be used set the key for
+decryption of the directory and its descendants.
+
+.ft R
+# ls -l /encrypted/dir/
+ -rw-r--r--. 1 root root 0 Mar 5 21:41 zbx7tsUEMLzh+AUVMkQcnB
+
+# f2fscrypt get_policy /encrypted/dir/
+ /encrypted/dir/: 28e21cc0c4393da1
+
+# f2fscrypt add_key -S 0x1234
+ Enter passphrase (echo disabled):
+ Added key with descriptor [28e21cc0c4393da1]
+
+# ls -l /encrypted/dir/
+ -rw-r--r--. 1 root root 0 Mar 5 21:41 test.txt
+
+.nf
+Show process keyrings.
+
+.ft R
+# keyctl show
+ Session Keyring
+ 84022412 --alswrv 0 0 keyring: _ses
+ 204615789 --alswrv 0 65534 \\_ keyring: _uid.0
+ 529474961 --alsw-v 0 0 \\_ logon: f2fs:28e21cc0c4393da1
+
+.SH AUTHOR
+Written by Kinglong Mee <kinglongmee@gmail.com>,
+Migrated from e4crypt that Written by Michael Halcrow <mhalcrow@google.com>,
+Ildar Muslukhov <muslukhovi@gmail.com>, and Theodore Ts'o <tytso@mit.edu>
+.SH SEE ALSO
+.BR keyctl (1),
+.BR mkfs.f2fs (8),
+.BR mount (8).
diff --git a/tools/f2fscrypt.c b/tools/f2fscrypt.c
new file mode 100644
index 0000000..48ea5f6
--- /dev/null
+++ b/tools/f2fscrypt.c
@@ -0,0 +1,916 @@
+/*
+ * f2fscrypt.c - f2fs encryption management utility
+ *
+ * Authors: Kinglong Mee <kinglongmee@gmail.com>
+ *
+ * Copied from e4crypt that for ext4 filesystem.
+ * Authors: Michael Halcrow <mhalcrow@google.com>,
+ * Ildar Muslukhov <ildarm@google.com>
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <linux/fs.h>
+#include <uuid/uuid.h>
+
+#if !defined(HAVE_ADD_KEY) || !defined(HAVE_KEYCTL)
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_KEY_H
+#include <sys/key.h>
+#endif
+
+#define F2FS_MAX_KEY_SIZE 64
+#define F2FS_MAX_PASSPHRASE_SIZE 1024
+#define F2FS_MAX_SALT_SIZE 256
+
+/* Encryption algorithms, key size and key reference len */
+#define F2FS_ENCRYPTION_MODE_INVALID 0
+#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1
+#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2
+#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3
+#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4
+
+#define F2FS_AES_256_XTS_KEY_SIZE 64
+#define F2FS_AES_256_GCM_KEY_SIZE 32
+#define F2FS_AES_256_CBC_KEY_SIZE 32
+#define F2FS_AES_256_CTS_KEY_SIZE 32
+#define F2FS_MAX_KEY_SIZE 64
+
+/* Password derivation constants */
+#define F2FS_MAX_PASSPHRASE_SIZE 1024
+#define F2FS_MAX_SALT_SIZE 256
+#define F2FS_PBKDF2_ITERATIONS 0xFFFF
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING -1
+#define KEY_SPEC_PROCESS_KEYRING -2
+#define KEY_SPEC_SESSION_KEYRING -3
+#define KEY_SPEC_USER_KEYRING -4
+#define KEY_SPEC_USER_SESSION_KEYRING -5
+#define KEY_SPEC_GROUP_KEYRING -6
+
+#define KEYCTL_GET_KEYRING_ID 0
+#define KEYCTL_JOIN_SESSION_KEYRING 1
+#define KEYCTL_DESCRIBE 6
+#define KEYCTL_SEARCH 10
+#define KEYCTL_SESSION_TO_PARENT 18
+
+/*
+ * File system encryption support
+ */
+/* Policy provided via an ioctl on the topmost directory */
+#define F2FS_KEY_DESCRIPTOR_SIZE 8
+#define F2FS_KEY_REF_STR_BUF_SIZE ((F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1)
+
+struct f2fs_fscrypt_policy {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
+} __attribute__((packed));
+
+#define F2FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct f2fs_fscrypt_policy)
+#define F2FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
+#define F2FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct f2fs_fscrypt_policy)
+
+typedef int32_t key_serial_t;
+
+
+
+#define OPT_VERBOSE 0x0001
+#define OPT_QUIET 0x0002
+
+struct f2fs_encryption_key {
+ __u32 mode;
+ char raw[F2FS_MAX_KEY_SIZE];
+ __u32 size;
+} __attribute__((__packed__));
+
+int options;
+
+extern void f2fs_sha512(const unsigned char *in, unsigned long in_size,
+ unsigned char *out);
+
+#ifndef HAVE_KEYCTL
+static long keyctl(int cmd, ...)
+{
+ va_list va;
+ unsigned long arg2, arg3, arg4, arg5;
+
+ va_start(va, cmd);
+ arg2 = va_arg(va, unsigned long);
+ arg3 = va_arg(va, unsigned long);
+ arg4 = va_arg(va, unsigned long);
+ arg5 = va_arg(va, unsigned long);
+ va_end(va);
+ return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+#endif
+
+#ifndef HAVE_ADD_KEY
+static key_serial_t add_key(const char *type, const char *description,
+ const void *payload, size_t plen,
+ key_serial_t keyring)
+{
+ return syscall(__NR_add_key, type, description, payload,
+ plen, keyring);
+}
+#endif
+
+static const unsigned char *hexchars = (const unsigned char *) "0123456789abcdef";
+static const size_t hexchars_size = 16;
+
+#define SHA512_LENGTH 64
+#define F2FS_KEY_TYPE_LOGON "logon"
+#define F2FS_KEY_DESC_PREFIX "f2fs:"
+#define F2FS_KEY_DESC_PREFIX_SIZE 5
+
+static int int_log2(int arg)
+{
+ int l = 0;
+
+ arg >>= 1;
+ while (arg) {
+ l++;
+ arg >>= 1;
+ }
+ return l;
+}
+
+static void validate_paths(int argc, char *argv[], int path_start_index)
+{
+ int x;
+ int valid = 1;
+ struct stat st;
+
+ for (x = path_start_index; x < argc; x++) {
+ int ret = access(argv[x], W_OK);
+ if (ret) {
+ invalid:
+ perror(argv[x]);
+ valid = 0;
+ continue;
+ }
+ ret = stat(argv[x], &st);
+ if (ret < 0)
+ goto invalid;
+ if (!S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "%s is not a directory\n", argv[x]);
+ goto invalid;
+ }
+ }
+ if (!valid)
+ exit(1);
+}
+
+static int hex2byte(const char *hex, size_t hex_size, unsigned char *bytes,
+ size_t bytes_size)
+{
+ size_t x;
+ unsigned char *h, *l;
+
+ if (hex_size % 2)
+ return -EINVAL;
+ for (x = 0; x < hex_size; x += 2) {
+ h = memchr(hexchars, hex[x], hexchars_size);
+ if (!h)
+ return -EINVAL;
+ l = memchr(hexchars, hex[x + 1], hexchars_size);
+ if (!l)
+ return -EINVAL;
+ if ((x >> 1) >= bytes_size)
+ return -EINVAL;
+ bytes[x >> 1] = (((unsigned char)(h - hexchars) << 4) +
+ (unsigned char)(l - hexchars));
+ }
+ return 0;
+}
+
+/*
+ * Salt handling
+ */
+struct salt {
+ unsigned char *salt;
+ char key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE];
+ unsigned char key_desc[F2FS_KEY_DESCRIPTOR_SIZE];
+ unsigned char key[F2FS_MAX_KEY_SIZE];
+ size_t salt_len;
+};
+struct salt *salt_list;
+unsigned num_salt;
+unsigned max_salt;
+char in_passphrase[F2FS_MAX_PASSPHRASE_SIZE];
+
+static struct salt *find_by_salt(unsigned char *salt, size_t salt_len)
+{
+ unsigned int i;
+ struct salt *p;
+
+ for (i = 0, p = salt_list; i < num_salt; i++, p++)
+ if ((p->salt_len == salt_len) &&
+ !memcmp(p->salt, salt, salt_len))
+ return p;
+ return NULL;
+}
+
+static void add_salt(unsigned char *salt, size_t salt_len)
+{
+ if (find_by_salt(salt, salt_len))
+ return;
+ if (num_salt >= max_salt) {
+ max_salt = num_salt + 10;
+ salt_list = realloc(salt_list, max_salt * sizeof(struct salt));
+ if (!salt_list) {
+ fprintf(stderr, "Couldn't allocate salt list\n");
+ exit(1);
+ }
+ }
+ salt_list[num_salt].salt = salt;
+ salt_list[num_salt].salt_len = salt_len;
+ num_salt++;
+}
+
+static void clear_secrets(void)
+{
+ if (salt_list) {
+ memset(salt_list, 0, sizeof(struct salt) * max_salt);
+ free(salt_list);
+ salt_list = NULL;
+ }
+ memset(in_passphrase, 0, sizeof(in_passphrase));
+}
+
+static void die_signal_handler(int signum, siginfo_t *siginfo, void *context)
+{
+ clear_secrets();
+ exit(-1);
+}
+
+static void sigcatcher_setup(void)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_sigaction = die_signal_handler;
+ sa.sa_flags = SA_SIGINFO;
+
+ sigaction(SIGHUP, &sa, 0);
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGQUIT, &sa, 0);
+ sigaction(SIGFPE, &sa, 0);
+ sigaction(SIGILL, &sa, 0);
+ sigaction(SIGBUS, &sa, 0);
+ sigaction(SIGSEGV, &sa, 0);
+ sigaction(SIGABRT, &sa, 0);
+ sigaction(SIGPIPE, &sa, 0);
+ sigaction(SIGALRM, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+ sigaction(SIGUSR1, &sa, 0);
+ sigaction(SIGUSR2, &sa, 0);
+ sigaction(SIGPOLL, &sa, 0);
+ sigaction(SIGPROF, &sa, 0);
+ sigaction(SIGSYS, &sa, 0);
+ sigaction(SIGTRAP, &sa, 0);
+ sigaction(SIGVTALRM, &sa, 0);
+ sigaction(SIGXCPU, &sa, 0);
+ sigaction(SIGXFSZ, &sa, 0);
+}
+
+
+#define PARSE_FLAGS_NOTSUPP_OK 0x0001
+#define PARSE_FLAGS_FORCE_FN 0x0002
+
+static void parse_salt(char *salt_str, int flags)
+{
+ unsigned char buf[F2FS_MAX_SALT_SIZE];
+ char *cp = salt_str;
+ unsigned char *salt_buf;
+ int fd, ret, salt_len = 0;
+
+ if (flags & PARSE_FLAGS_FORCE_FN)
+ goto salt_from_filename;
+ if (strncmp(cp, "s:", 2) == 0) {
+ cp += 2;
+ salt_len = strlen(cp);
+ if (salt_len >= F2FS_MAX_SALT_SIZE)
+ goto invalid_salt;
+ strncpy((char *) buf, cp, sizeof(buf));
+ } else if (cp[0] == '/') {
+ salt_from_filename:
+ fd = open(cp, O_RDONLY | O_DIRECTORY);
+ if (fd == -1 && errno == ENOTDIR)
+ fd = open(cp, O_RDONLY);
+ if (fd == -1) {
+ perror(cp);
+ exit(1);
+ }
+ ret = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT, &buf);
+ close(fd);
+ if (ret < 0) {
+ if (flags & PARSE_FLAGS_NOTSUPP_OK)
+ return;
+ perror("F2FS_IOC_GET_ENCRYPTION_PWSALT");
+ exit(1);
+ }
+ if (options & OPT_VERBOSE) {
+ char tmp[80];
+ uuid_unparse(buf, tmp);
+ printf("%s has pw salt %s\n", cp, tmp);
+ }
+ salt_len = 16;
+ } else if (strncmp(cp, "f:", 2) == 0) {
+ cp += 2;
+ goto salt_from_filename;
+ } else if (strncmp(cp, "0x", 2) == 0) {
+ unsigned char *h, *l;
+
+ cp += 2;
+ if (strlen(cp) & 1)
+ goto invalid_salt;
+ while (*cp) {
+ if (salt_len >= F2FS_MAX_SALT_SIZE)
+ goto invalid_salt;
+ h = memchr(hexchars, *cp++, hexchars_size);
+ l = memchr(hexchars, *cp++, hexchars_size);
+ if (!h || !l)
+ goto invalid_salt;
+ buf[salt_len++] =
+ (((unsigned char)(h - hexchars) << 4) +
+ (unsigned char)(l - hexchars));
+ }
+ } else if (uuid_parse(cp, buf) == 0) {
+ salt_len = 16;
+ } else {
+ invalid_salt:
+ fprintf(stderr, "Invalid salt: %s\n", salt_str);
+ exit(1);
+ }
+ salt_buf = malloc(salt_len);
+ if (!salt_buf) {
+ fprintf(stderr, "Couldn't allocate salt\n");
+ exit(1);
+ }
+ memcpy(salt_buf, buf, salt_len);
+ add_salt(salt_buf, salt_len);
+}
+
+static void set_policy(struct salt *set_salt, int pad,
+ int argc, char *argv[], int path_start_index)
+{
+ struct salt *salt;
+ struct f2fs_fscrypt_policy policy;
+ uuid_t uu;
+ int fd;
+ int x;
+ int rc;
+
+ if ((pad != 4) && (pad != 8) &&
+ (pad != 16) && (pad != 32)) {
+ fprintf(stderr, "Invalid padding %d\n", pad);
+ exit(1);
+ }
+
+ for (x = path_start_index; x < argc; x++) {
+ fd = open(argv[x], O_DIRECTORY);
+ if (fd == -1) {
+ perror(argv[x]);
+ exit(1);
+ }
+ if (set_salt)
+ salt = set_salt;
+ else {
+ if (ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT,
+ &uu) < 0) {
+ perror("F2FS_IOC_GET_ENCRYPTION_PWSALT");
+ exit(1);
+ }
+ salt = find_by_salt(uu, sizeof(uu));
+ if (!salt) {
+ fprintf(stderr, "Couldn't find salt!?!\n");
+ exit(1);
+ }
+ }
+ policy.version = 0;
+ policy.contents_encryption_mode =
+ F2FS_ENCRYPTION_MODE_AES_256_XTS;
+ policy.filenames_encryption_mode =
+ F2FS_ENCRYPTION_MODE_AES_256_CTS;
+ policy.flags = int_log2(pad >> 2);
+ memcpy(policy.master_key_descriptor, salt->key_desc,
+ F2FS_KEY_DESCRIPTOR_SIZE);
+ rc = ioctl(fd, F2FS_IOC_SET_ENCRYPTION_POLICY, &policy);
+ close(fd);
+ if (rc) {
+ printf("Error [%s] setting policy.\nThe key descriptor "
+ "[%s] may not match the existing encryption "
+ "context for directory [%s].\n",
+ strerror(errno), salt->key_ref_str, argv[x]);
+ continue;
+ }
+ printf("Key with descriptor [%s] applied to %s.\n",
+ salt->key_ref_str, argv[x]);
+ }
+}
+
+static void pbkdf2_sha512(const char *passphrase, struct salt *salt,
+ unsigned int count,
+ unsigned char derived_key[F2FS_MAX_KEY_SIZE])
+{
+ size_t passphrase_size = strlen(passphrase);
+ unsigned char buf[SHA512_LENGTH + F2FS_MAX_PASSPHRASE_SIZE] = {0};
+ unsigned char tempbuf[SHA512_LENGTH] = {0};
+ char final[SHA512_LENGTH] = {0};
+ unsigned char saltbuf[F2FS_MAX_SALT_SIZE + F2FS_MAX_PASSPHRASE_SIZE] = {0};
+ int actual_buf_len = SHA512_LENGTH + passphrase_size;
+ int actual_saltbuf_len = F2FS_MAX_SALT_SIZE + passphrase_size;
+ unsigned int x, y;
+ __u32 *final_u32 = (__u32 *)final;
+ __u32 *temp_u32 = (__u32 *)tempbuf;
+
+ if (passphrase_size > F2FS_MAX_PASSPHRASE_SIZE) {
+ printf("Passphrase size is %zd; max is %d.\n", passphrase_size,
+ F2FS_MAX_PASSPHRASE_SIZE);
+ exit(1);
+ }
+ if (salt->salt_len > F2FS_MAX_SALT_SIZE) {
+ printf("Salt size is %zd; max is %d.\n", salt->salt_len,
+ F2FS_MAX_SALT_SIZE);
+ exit(1);
+ }
+ assert(F2FS_MAX_KEY_SIZE <= SHA512_LENGTH);
+
+ memcpy(saltbuf, salt->salt, salt->salt_len);
+ memcpy(&saltbuf[F2FS_MAX_SALT_SIZE], passphrase, passphrase_size);
+
+ memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size);
+
+ for (x = 0; x < count; ++x) {
+ if (x == 0) {
+ f2fs_sha512(saltbuf, actual_saltbuf_len, tempbuf);
+ } else {
+ /*
+ * buf: [previous hash || passphrase]
+ */
+ memcpy(buf, tempbuf, SHA512_LENGTH);
+ f2fs_sha512(buf, actual_buf_len, tempbuf);
+ }
+ for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y)
+ final_u32[y] = final_u32[y] ^ temp_u32[y];
+ }
+ memcpy(derived_key, final, F2FS_MAX_KEY_SIZE);
+}
+
+static int disable_echo(struct termios *saved_settings)
+{
+ struct termios current_settings;
+ int rc = 0;
+
+ rc = tcgetattr(0, ¤t_settings);
+ if (rc)
+ return rc;
+ *saved_settings = current_settings;
+ current_settings.c_lflag &= ~ECHO;
+ rc = tcsetattr(0, TCSANOW, ¤t_settings);
+
+ return rc;
+}
+
+static void get_passphrase(char *passphrase, int len)
+{
+ char *p;
+ struct termios current_settings;
+
+ assert(len > 0);
+ disable_echo(¤t_settings);
+ p = fgets(passphrase, len, stdin);
+ tcsetattr(0, TCSANOW, ¤t_settings);
+ printf("\n");
+ if (!p) {
+ printf("Aborting.\n");
+ exit(1);
+ }
+ p = strrchr(passphrase, '\n');
+ if (!p)
+ p = passphrase + len - 1;
+ *p = '\0';
+}
+
+struct keyring_map {
+ char name[4];
+ size_t name_len;
+ int code;
+};
+
+static const struct keyring_map keyrings[] = {
+ {"@us", 3, KEY_SPEC_USER_SESSION_KEYRING},
+ {"@u", 2, KEY_SPEC_USER_KEYRING},
+ {"@s", 2, KEY_SPEC_SESSION_KEYRING},
+ {"@g", 2, KEY_SPEC_GROUP_KEYRING},
+ {"@p", 2, KEY_SPEC_PROCESS_KEYRING},
+ {"@t", 2, KEY_SPEC_THREAD_KEYRING},
+};
+
+static int get_keyring_id(const char *keyring)
+{
+ unsigned int x;
+ char *end;
+
+ /*
+ * If no keyring is specified, by default use either the user
+ * session key ring or the session keyring. Fetching the
+ * session keyring will return the user session keyring if no
+ * session keyring has been set.
+ *
+ * We need to do this instead of simply adding the key to
+ * KEY_SPEC_SESSION_KEYRING since trying to add a key to a
+ * session keyring that does not yet exist will cause the
+ * kernel to create a session keyring --- which wil then get
+ * garbage collected as soon as f2fscrypt exits.
+ *
+ * The fact that the keyctl system call and the add_key system
+ * call treats KEY_SPEC_SESSION_KEYRING differently when a
+ * session keyring does not exist is very unfortunate and
+ * confusing, but so it goes...
+ */
+ if (keyring == NULL)
+ return keyctl(KEYCTL_GET_KEYRING_ID,
+ KEY_SPEC_SESSION_KEYRING, 0);
+ for (x = 0; x < (sizeof(keyrings) / sizeof(keyrings[0])); ++x) {
+ if (strcmp(keyring, keyrings[x].name) == 0) {
+ return keyrings[x].code;
+ }
+ }
+ x = strtoul(keyring, &end, 10);
+ if (*end == '\0') {
+ if (keyctl(KEYCTL_DESCRIBE, x, NULL, 0) < 0)
+ return 0;
+ return x;
+ }
+ return 0;
+}
+
+static void generate_key_ref_str(struct salt *salt)
+{
+ unsigned char key_ref1[SHA512_LENGTH];
+ unsigned char key_ref2[SHA512_LENGTH];
+ int x;
+
+ f2fs_sha512(salt->key, F2FS_MAX_KEY_SIZE, key_ref1);
+ f2fs_sha512(key_ref1, SHA512_LENGTH, key_ref2);
+ memcpy(salt->key_desc, key_ref2, F2FS_KEY_DESCRIPTOR_SIZE);
+ for (x = 0; x < F2FS_KEY_DESCRIPTOR_SIZE; ++x) {
+ sprintf(&salt->key_ref_str[x * 2], "%02x",
+ salt->key_desc[x]);
+ }
+ salt->key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE - 1] = '\0';
+}
+
+static void insert_key_into_keyring(const char *keyring, struct salt *salt)
+{
+ int keyring_id = get_keyring_id(keyring);
+ struct f2fs_encryption_key key;
+ char key_ref_full[F2FS_KEY_DESC_PREFIX_SIZE +
+ F2FS_KEY_REF_STR_BUF_SIZE];
+ int rc;
+
+ if (keyring_id == 0) {
+ printf("Invalid keyring [%s].\n", keyring);
+ exit(1);
+ }
+ sprintf(key_ref_full, "%s%s", F2FS_KEY_DESC_PREFIX,
+ salt->key_ref_str);
+ rc = keyctl(KEYCTL_SEARCH, keyring_id, F2FS_KEY_TYPE_LOGON,
+ key_ref_full, 0);
+ if (rc != -1) {
+ if ((options & OPT_QUIET) == 0)
+ printf("Key with descriptor [%s] already exists\n",
+ salt->key_ref_str);
+ return;
+ } else if ((rc == -1) && (errno != ENOKEY)) {
+ printf("keyctl_search failed: %s\n", strerror(errno));
+ if (errno == -EINVAL)
+ printf("Keyring [%s] is not available.\n", keyring);
+ exit(1);
+ }
+ key.mode = F2FS_ENCRYPTION_MODE_AES_256_XTS;
+ memcpy(key.raw, salt->key, F2FS_MAX_KEY_SIZE);
+ key.size = F2FS_MAX_KEY_SIZE;
+ rc = add_key(F2FS_KEY_TYPE_LOGON, key_ref_full, (void *)&key,
+ sizeof(key), keyring_id);
+ if (rc == -1) {
+ if (errno == EDQUOT) {
+ printf("Error adding key to keyring; quota exceeded\n");
+ } else {
+ printf("Error adding key with key descriptor [%s]: "
+ "%s\n", salt->key_ref_str, strerror(errno));
+ }
+ exit(1);
+ } else {
+ if ((options & OPT_QUIET) == 0)
+ printf("Added key with descriptor [%s]\n",
+ salt->key_ref_str);
+ }
+}
+
+static void get_default_salts(void)
+{
+ FILE *f = setmntent("/etc/mtab", "r");
+ struct mntent *mnt;
+
+ while (f && ((mnt = getmntent(f)) != NULL)) {
+ if (strcmp(mnt->mnt_type, "f2fs") ||
+ access(mnt->mnt_dir, R_OK))
+ continue;
+ parse_salt(mnt->mnt_dir, PARSE_FLAGS_NOTSUPP_OK);
+ }
+ endmntent(f);
+}
+
+/* Functions which implement user commands */
+
+struct cmd_desc {
+ const char *cmd_name;
+ void (*cmd_func)(int, char **, const struct cmd_desc *);
+ const char *cmd_desc;
+ const char *cmd_help;
+ int cmd_flags;
+};
+
+#define CMD_HIDDEN 0x0001
+
+static void do_help(int argc, char **argv, const struct cmd_desc *cmd);
+
+#define add_key_desc "adds a key to the user's keyring"
+#define add_key_help \
+"f2fscrypt add_key -S salt [ -k keyring ] [-v] [-q] [ path ... ]\n\n" \
+"Prompts the user for a passphrase and inserts it into the specified\n" \
+"keyring. If no keyring is specified, f2fscrypt will use the session\n" \
+"keyring if it exists or the user session keyring if it does not.\n\n" \
+"If one or more directory paths are specified, f2fscrypt will try to\n" \
+"set the policy of those directories to use the key just entered by\n" \
+"the user.\n"
+
+static void do_add_key(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct salt *salt;
+ char *keyring = NULL;
+ int i, opt, pad = 4;
+ unsigned j;
+
+ while ((opt = getopt(argc, argv, "k:S:p:vq")) != -1) {
+ switch (opt) {
+ case 'k':
+ /* Specify a keyring. */
+ keyring = optarg;
+ break;
+ case 'p':
+ pad = atoi(optarg);
+ break;
+ case 'S':
+ /* Salt value for passphrase. */
+ parse_salt(optarg, 0);
+ break;
+ case 'v':
+ options |= OPT_VERBOSE;
+ break;
+ case 'q':
+ options |= OPT_QUIET;
+ break;
+ default:
+ fprintf(stderr, "Unrecognized option: %c\n", opt);
+ case '?':
+ fputs("USAGE:\n ", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+ }
+ if (num_salt == 0)
+ get_default_salts();
+ if (num_salt == 0) {
+ fprintf(stderr, "No salt values available\n");
+ exit(1);
+ }
+ validate_paths(argc, argv, optind);
+ for (i = optind; i < argc; i++)
+ parse_salt(argv[i], PARSE_FLAGS_FORCE_FN);
+ printf("Enter passphrase (echo disabled): ");
+ get_passphrase(in_passphrase, sizeof(in_passphrase));
+ for (j = 0, salt = salt_list; j < num_salt; j++, salt++) {
+ pbkdf2_sha512(in_passphrase, salt,
+ F2FS_PBKDF2_ITERATIONS, salt->key);
+ generate_key_ref_str(salt);
+ insert_key_into_keyring(keyring, salt);
+ }
+ if (optind != argc)
+ set_policy(NULL, pad, argc, argv, optind);
+ clear_secrets();
+ exit(0);
+}
+
+#define set_policy_desc "sets a policy for directories"
+#define set_policy_help \
+"f2fscrypt set_policy policy path ... \n\n" \
+"Sets the policy for the directories specified on the command line.\n" \
+"All directories must be empty to set the policy; if the directory\n" \
+"already has a policy established, f2fscrypt will validate that it the\n" \
+"policy matches what was specified. A policy is an encryption key\n" \
+"identifier consisting of 16 hexadecimal characters.\n"
+
+static void do_set_policy(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct salt saltbuf;
+ int c, pad = 4;
+
+ while ((c = getopt (argc, argv, "p:")) != EOF) {
+ switch (c) {
+ case 'p':
+ pad = atoi(optarg);
+ break;
+ }
+ }
+
+ if (argc < optind + 2) {
+ fprintf(stderr, "Missing required argument(s).\n\n");
+ fputs("USAGE:\n ", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ if ((strlen(argv[optind]) != (F2FS_KEY_DESCRIPTOR_SIZE * 2)) ||
+ hex2byte(argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2),
+ saltbuf.key_desc, F2FS_KEY_DESCRIPTOR_SIZE)) {
+ printf("Invalid key descriptor [%s]. Valid characters "
+ "are 0-9 and a-f, lower case. "
+ "Length must be %d.\n",
+ argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2));
+ exit(1);
+ }
+ validate_paths(argc, argv, optind+1);
+ strcpy(saltbuf.key_ref_str, argv[optind]);
+ set_policy(&saltbuf, pad, argc, argv, optind+1);
+ exit(0);
+}
+
+#define get_policy_desc "get the encryption for directories"
+#define get_policy_help \
+"f2fscrypt get_policy path ... \n\n" \
+"Gets the policy for the directories specified on the command line.\n"
+
+static void do_get_policy(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct f2fs_fscrypt_policy policy;
+ struct stat st;
+ int i, j, fd, rc;
+
+ if (argc < 2) {
+ fprintf(stderr, "Missing required argument(s).\n\n");
+ fputs("USAGE:\n ", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (stat(argv[i], &st) < 0) {
+ perror(argv[i]);
+ continue;
+ }
+ fd = open(argv[i],
+ S_ISDIR(st.st_mode) ? O_DIRECTORY : O_RDONLY);
+ if (fd == -1) {
+ perror(argv[i]);
+ exit(1);
+ }
+ rc = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_POLICY, &policy);
+ close(fd);
+ if (rc) {
+ printf("Error getting policy for %s: %s\n",
+ argv[i], strerror(errno));
+ continue;
+ }
+ printf("%s: ", argv[i]);
+ for (j = 0; j < F2FS_KEY_DESCRIPTOR_SIZE; j++) {
+ printf("%02x", (unsigned char) policy.master_key_descriptor[j]);
+ }
+ fputc('\n', stdout);
+ }
+ exit(0);
+}
+
+#define new_session_desc "give the invoking process a new session keyring"
+#define new_session_help \
+"f2fscrypt new_session\n\n" \
+"Give the invoking process (typically a shell) a new session keyring,\n" \
+"discarding its old session keyring.\n"
+
+static void do_new_session(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ long keyid, ret;
+
+ if (argc > 1) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+ keyid = keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL);
+ if (keyid < 0) {
+ perror("KEYCTL_JOIN_SESSION_KEYRING");
+ exit(1);
+ }
+ ret = keyctl(KEYCTL_SESSION_TO_PARENT, NULL);
+ if (ret < 0) {
+ perror("KEYCTL_SESSION_TO_PARENT");
+ exit(1);
+ }
+ printf("Switched invoking process to new session keyring %ld\n", keyid);
+ exit(0);
+}
+
+#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
+#define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
+
+const struct cmd_desc cmd_list[] = {
+ _CMD(help),
+ CMD(add_key),
+ CMD(get_policy),
+ CMD(new_session),
+ CMD(set_policy),
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+static void do_help(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ const struct cmd_desc *p;
+
+ if (argc > 1) {
+ for (p = cmd_list; p->cmd_name; p++) {
+ if (p->cmd_flags & CMD_HIDDEN)
+ continue;
+ if (strcmp(p->cmd_name, argv[1]) == 0) {
+ putc('\n', stdout);
+ fputs("USAGE:\n ", stdout);
+ fputs(p->cmd_help, stdout);
+ exit(0);
+ }
+ }
+ printf("Unknown command: %s\n\n", argv[1]);
+ }
+
+ fputs("Available commands:\n", stdout);
+ for (p = cmd_list; p->cmd_name; p++) {
+ if (p->cmd_flags & CMD_HIDDEN)
+ continue;
+ printf(" %-20s %s\n", p->cmd_name, p->cmd_desc);
+ }
+ printf("\nTo get more information on a command, "
+ "type 'f2fscrypt help cmd'\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ const struct cmd_desc *cmd;
+
+ if (argc < 2)
+ do_help(argc, argv, cmd_list);
+
+ sigcatcher_setup();
+ for (cmd = cmd_list; cmd->cmd_name; cmd++) {
+ if (strcmp(cmd->cmd_name, argv[1]) == 0) {
+ cmd->cmd_func(argc-1, argv+1, cmd);
+ exit(0);
+ }
+ }
+ printf("Unknown command: %s\n\n", argv[1]);
+ do_help(1, argv, cmd_list);
+ return 0;
+}
diff --git a/tools/sha512.c b/tools/sha512.c
new file mode 100644
index 0000000..bf0d9a4
--- /dev/null
+++ b/tools/sha512.c
@@ -0,0 +1,323 @@
+/*
+ * sha512.c --- The sha512 algorithm
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ * (copied from libtomcrypt and then relicensed under GPLv2)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <linux/fs.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define F2FS_SHA512_LENGTH 64
+
+/* the K array */
+#define CONST64(n) n
+static const __u64 K[80] = {
+ CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+ CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+ CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+ CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+ CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+ CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+ CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+ CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+ CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+ CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+ CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+ CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+ CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+ CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+ CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+ CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+ CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+ CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+ CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+ CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+ CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+ CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+ CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+ CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+ CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+ CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+ CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+ CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+ CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+ CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+ CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+ CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+ CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+ CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+ CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+ CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+ CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+ CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+ CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+ CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) ROR64c(x, n)
+#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)n))
+#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#define RND(a,b,c,d,e,f,g,h,i)\
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];\
+ t1 = Sigma0(a) + Maj(a, b, c);\
+ d += t0;\
+ h = t0 + t1;
+#define STORE64H(x, y) \
+ do { \
+ (y)[0] = (unsigned char)(((x)>>56)&255);\
+ (y)[1] = (unsigned char)(((x)>>48)&255);\
+ (y)[2] = (unsigned char)(((x)>>40)&255);\
+ (y)[3] = (unsigned char)(((x)>>32)&255);\
+ (y)[4] = (unsigned char)(((x)>>24)&255);\
+ (y)[5] = (unsigned char)(((x)>>16)&255);\
+ (y)[6] = (unsigned char)(((x)>>8)&255);\
+ (y)[7] = (unsigned char)((x)&255); } while(0)
+
+#define LOAD64H(x, y)\
+ do {x = \
+ (((__u64)((y)[0] & 255)) << 56) |\
+ (((__u64)((y)[1] & 255)) << 48) |\
+ (((__u64)((y)[2] & 255)) << 40) |\
+ (((__u64)((y)[3] & 255)) << 32) |\
+ (((__u64)((y)[4] & 255)) << 24) |\
+ (((__u64)((y)[5] & 255)) << 16) |\
+ (((__u64)((y)[6] & 255)) << 8) |\
+ (((__u64)((y)[7] & 255)));\
+ } while(0)
+
+#define ROR64c(x, y) \
+ ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)(y)&CONST64(63))) | \
+ ((x)<<((__u64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+struct sha512_state {
+ __u64 length, state[8];
+ unsigned long curlen;
+ unsigned char buf[128];
+};
+
+/* This is a highly simplified version from libtomcrypt */
+struct hash_state {
+ struct sha512_state sha512;
+};
+
+static void sha512_compress(struct hash_state * md, const unsigned char *buf)
+{
+ __u64 S[8], W[80], t0, t1;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->sha512.state[i];
+ }
+
+ /* copy the state into 1024-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD64H(W[i], buf + (8*i));
+ }
+
+ /* fill W[16..79] */
+ for (i = 16; i < 80; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] +
+ Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ for (i = 0; i < 80; i += 8) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->sha512.state[i] = md->sha512.state[i] + S[i];
+ }
+}
+
+static void sha512_init(struct hash_state * md)
+{
+ md->sha512.curlen = 0;
+ md->sha512.length = 0;
+ md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
+ md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
+ md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
+ md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
+ md->sha512.state[4] = CONST64(0x510e527fade682d1);
+ md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
+ md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
+ md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+static void sha512_done(struct hash_state * md, unsigned char *out)
+{
+ int i;
+
+ /* increase the length of the message */
+ md->sha512.length += md->sha512.curlen * CONST64(8);
+
+ /* append the '1' bit */
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 112 bytes we append zeros then
+ * compress. Then we can fall back to padding zeros and length encoding
+ * like normal. */
+ if (md->sha512.curlen > 112) {
+ while (md->sha512.curlen < 128) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+ sha512_compress(md, md->sha512.buf);
+ md->sha512.curlen = 0;
+ }
+
+ /* pad upto 120 bytes of zeroes note: that from 112 to 120 is the 64 MSB
+ * of the length. We assume that you won't hash > 2^64 bits of data. */
+ while (md->sha512.curlen < 120) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha512.length, md->sha512.buf + 120);
+ sha512_compress(md, md->sha512.buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE64H(md->sha512.state[i], out+(8 * i));
+ }
+}
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#define SHA512_BLOCKSIZE 128
+static void sha512_process(struct hash_state * md,
+ const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ while (inlen > 0) {
+ if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) {
+ sha512_compress(md, in);
+ md->sha512.length += SHA512_BLOCKSIZE * 8;
+ in += SHA512_BLOCKSIZE;
+ inlen -= SHA512_BLOCKSIZE;
+ } else {
+ n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen));
+ memcpy(md->sha512.buf + md->sha512.curlen,
+ in, (size_t)n);
+ md->sha512.curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->sha512.curlen == SHA512_BLOCKSIZE) {
+ sha512_compress(md, md->sha512.buf);
+ md->sha512.length += SHA512_BLOCKSIZE * 8;
+ md->sha512.curlen = 0;
+ }
+ }
+ }
+}
+
+void f2fs_sha512(const unsigned char *in, unsigned long in_size,
+ unsigned char out[F2FS_SHA512_LENGTH])
+{
+ struct hash_state md;
+
+ sha512_init(&md);
+ sha512_process(&md, in, in_size);
+ sha512_done(&md, out);
+}
+
+#ifdef UNITTEST
+static const struct {
+ char *msg;
+ unsigned char hash[64];
+} tests[] = {
+ { "",
+ { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd,
+ 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
+ 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc,
+ 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
+ 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0,
+ 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
+ 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81,
+ 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e }
+ },
+ { "abc",
+ { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f }
+ },
+ { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+ 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+ 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+ 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+ 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+ 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+ 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+ 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 }
+ },
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+ int errors = 0;
+ unsigned char tmp[64];
+ struct hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ unsigned char *msg = (unsigned char *) tests[i].msg;
+ int len = strlen(tests[i].msg);
+
+ f2fs_sha512(msg, len, tmp);
+ printf("SHA512 test message %d: ", i);
+ if (memcmp(tmp, tests[i].hash, 64) != 0) {
+ printf("FAILED\n");
+ errors++;
+ } else
+ printf("OK\n");
+ }
+ return errors;
+}
+
+#endif /* UNITTEST */