aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:34:37 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:34:37 +0000
commit6a97bd6674a30b18cc82c05cfea6a5bdd0e6416e (patch)
tree1908951ac5c6d879e06132ab33fdae3e86cf96ee
parent06727cadff1dfba9e2aa89689dca906711ff5be0 (diff)
parente8e82b5f06dc82e5019fe5aec5925ef9f533f665 (diff)
downloadf2fs-tools-android12-mainline-tethering-release.tar.gz
Change-Id: I1d2da4b1e8c6e9c2cf1babfdd5c8061aa990f26d
-rw-r--r--Android.bp81
-rw-r--r--METADATA9
-rw-r--r--README10
-rw-r--r--VERSION4
-rw-r--r--configure.ac21
-rw-r--r--fsck/Makefile.am10
-rw-r--r--fsck/compress.c178
-rw-r--r--fsck/compress.h22
-rw-r--r--fsck/defrag.c2
-rw-r--r--fsck/dir.c94
-rw-r--r--fsck/dump.c10
-rw-r--r--fsck/f2fs.h10
-rw-r--r--fsck/fsck.c72
-rw-r--r--fsck/fsck.h26
-rw-r--r--fsck/main.c322
-rw-r--r--fsck/mkquota.c1
-rw-r--r--fsck/mount.c162
-rw-r--r--fsck/node.c6
-rw-r--r--fsck/node.h11
-rw-r--r--fsck/quotaio.c10
-rw-r--r--fsck/quotaio.h13
-rw-r--r--fsck/quotaio_tree.c14
-rw-r--r--fsck/quotaio_v2.c53
-rw-r--r--fsck/resize.c25
-rw-r--r--fsck/segment.c243
-rw-r--r--fsck/sload.c32
-rw-r--r--fsck/xattr.c2
-rw-r--r--include/android_config.h4
-rw-r--r--include/f2fs_fs.h167
-rw-r--r--lib/libf2fs.c26
-rw-r--r--lib/libf2fs_io.c13
-rw-r--r--lib/libf2fs_zoned.c55
-rw-r--r--man/Makefile.am2
-rw-r--r--man/defrag.f2fs.82
-rw-r--r--man/f2fslabel.833
-rw-r--r--man/mkfs.f2fs.833
-rw-r--r--man/sload.f2fs.8101
-rw-r--r--mkfs/Makefile.am3
-rw-r--r--mkfs/f2fs_format.c204
-rw-r--r--mkfs/f2fs_format_main.c45
-rw-r--r--tools/Makefile.am2
-rw-r--r--tools/check_f2fs.c4
-rw-r--r--tools/f2fs_io/Android.bp15
-rw-r--r--tools/f2fs_io/Makefile.am2
-rw-r--r--tools/f2fs_io/f2fs_io.c340
-rw-r--r--tools/f2fs_io/f2fs_io.h61
-rw-r--r--tools/f2fs_io_parse.c2
-rw-r--r--tools/f2fscrypt.82
-rw-r--r--tools/f2fscrypt.c2
-rw-r--r--tools/sg_write_buffer/Android.bp10
50 files changed, 2316 insertions, 255 deletions
diff --git a/Android.bp b/Android.bp
index 0fa3305..2b09807 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,12 +1,51 @@
// Copyright 2017 The Android Open Source Project
+package {
+ default_applicable_licenses: ["external_f2fs-tools_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_f2fs-tools_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-GPL",
+ "SPDX-license-identifier-GPL-2.0",
+ "SPDX-license-identifier-LGPL",
+ "SPDX-license-identifier-LGPL-2.1",
+ ],
+ license_text: [
+ "COPYING",
+ "NOTICE",
+ ],
+}
+
cc_defaults {
name: "f2fs-tools-defaults",
cflags: [
"-DF2FS_MAJOR_VERSION=1",
- "-DF2FS_MINOR_VERSION=13",
- "-DF2FS_TOOLS_VERSION=\"1.13.0\"",
- "-DF2FS_TOOLS_DATE=\"2019-09-24\"",
+ "-DF2FS_MINOR_VERSION=14",
+ "-DF2FS_TOOLS_VERSION=\"1.14.0\"",
+ "-DF2FS_TOOLS_DATE=\"2020-08-24\"",
"-DWITH_ANDROID",
"-Wall",
"-Werror",
@@ -25,6 +64,7 @@ cc_defaults {
],
include_dirs: [
"external/e2fsprogs/lib/",
+ "external/e2fsprogs/lib/uuid",
"system/core/libsparse/include",
],
target: {
@@ -70,6 +110,7 @@ cc_defaults {
"fsck/mount.c",
"lib/libf2fs.c",
"lib/libf2fs_io.c",
+ "lib/libf2fs_zoned.c",
"lib/nls_utf8.c",
"fsck/dump.c",
],
@@ -103,8 +144,8 @@ cc_library_host_static {
},
}
-cc_binary {
- name: "make_f2fs",
+cc_defaults {
+ name: "make_f2fs_defaults",
defaults: [
"f2fs-tools-defaults",
"make_f2fs_src_files",
@@ -142,6 +183,28 @@ cc_binary {
}
cc_binary {
+ name: "make_f2fs",
+ defaults: [
+ "make_f2fs_defaults",
+ ],
+}
+
+cc_binary_host {
+ name: "make_f2fs_casefold",
+ defaults: [
+ "make_f2fs_defaults",
+ ],
+ target: {
+ host: {
+ cflags: ["-DCONF_CASEFOLD", "-DCONF_PROJID"],
+ },
+ windows: {
+ cflags: ["-DCONF_CASEFOLD", "-DCONF_PROJID"],
+ },
+ },
+}
+
+cc_binary {
name: "fsck.f2fs",
defaults: [
"f2fs-tools-defaults",
@@ -167,7 +230,11 @@ cc_binary {
host_supported: true,
recovery_available: true,
cflags: ["-DWITH_SLOAD"],
- srcs: ["fsck/fsck.c", "fsck/sload.c"],
+ srcs: [
+ "fsck/fsck.c",
+ "fsck/sload.c",
+ "fsck/compress.c",
+ ],
target: {
android: {
shared_libs: [
@@ -178,6 +245,7 @@ cc_binary {
"libselinux",
"libcutils",
"liblog",
+ "liblz4",
],
},
host: {
@@ -190,6 +258,7 @@ cc_binary {
"libcutils",
"liblog",
"libz",
+ "liblz4",
],
},
},
diff --git a/METADATA b/METADATA
index 7fc26ba..0120fd9 100644
--- a/METADATA
+++ b/METADATA
@@ -8,10 +8,11 @@ third_party {
type: GIT
value: "https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git"
}
- version: "v1.13.0"
+ version: "v1.14.0"
+ license_type: RESTRICTED
last_upgrade_date {
- year: 2020
- month: 03
- day: 31
+ year: 2021
+ month: 6
+ day: 3
}
}
diff --git a/README b/README
index 4ea3356..afe334f 100644
--- a/README
+++ b/README
@@ -1,8 +1,8 @@
-F2FS format utilility
+F2FS format utility
---------------------
-To use f2fs filesystem, you should format the storage partition
-with this utilility. Otherwise, you cannot mount f2fs.
+To use the f2fs filesystem, you should format the storage partition
+with this utility. Otherwise, you cannot mount f2fs.
Before compilation
------------------
@@ -17,7 +17,7 @@ You should install the following packages.
Initial compilation
-------------------
-Before compilation initially, autoconf/automake tools should be run.
+Before initial compilation, autoconf/automake tools should be run.
# ./autogen.sh
@@ -47,4 +47,4 @@ How to run by default
$ mkfs.f2fs -l [LABEL] $DEV
-For more mkfs options, see man page.
+For more mkfs options, see the man page.
diff --git a/VERSION b/VERSION
index 3ccde23..9043463 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-1.13.0
-2019-09-24
+1.14.0
+2020-08-24
diff --git a/configure.ac b/configure.ac
index 9ac0c24..32e97a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,6 +52,18 @@ AC_PATH_PROG([LDCONFIG], [ldconfig],
[$PATH:/sbin])
# Checks for libraries.
+AC_CHECK_LIB([lzo2], [main],
+ [AC_SUBST([liblzo2_LIBS], ["-llzo2"])
+ AC_DEFINE([HAVE_LIBLZO2], [1],
+ [Define if you have liblzo2])
+ ], [], [])
+
+AC_CHECK_LIB([lz4], [main],
+ [AC_SUBST([liblz4_LIBS], ["-llz4"])
+ AC_DEFINE([HAVE_LIBLZ4], [1],
+ [Define if you have liblz4])
+ ], [], [])
+
PKG_CHECK_MODULES([libuuid], [uuid])
AS_IF([test "x$with_selinux" != "xno"],
@@ -93,6 +105,7 @@ AC_CHECK_HEADERS(m4_flatten([
linux/posix_acl.h
linux/types.h
linux/xattr.h
+ linux/fiemap.h
mach/mach_time.h
mntent.h
scsi/sg.h
@@ -213,13 +226,17 @@ AC_CONFIG_FILES([
tools/f2fs_io/Makefile
])
+AC_CHECK_MEMBER([struct blk_zone.capacity],
+ [AC_DEFINE(HAVE_BLK_ZONE_REP_V2, [1], [report zones includes zone capacity])],
+ [], [[#include <linux/blkzoned.h>]])
+
# export library version info for mkfs/libf2fs_format_la
-AC_SUBST(FMT_CURRENT, 6)
+AC_SUBST(FMT_CURRENT, 7)
AC_SUBST(FMT_REVISION, 0)
AC_SUBST(FMT_AGE, 0)
# export library version info for lib/libf2fs_la
-AC_SUBST(LIBF2FS_CURRENT, 7)
+AC_SUBST(LIBF2FS_CURRENT, 8)
AC_SUBST(LIBF2FS_REVISION, 0)
AC_SUBST(LIBF2FS_AGE, 0)
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 1fc7310..e31d416 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -3,15 +3,19 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
sbin_PROGRAMS = fsck.f2fs
-noinst_HEADERS = common.h dict.h dqblk_v2.h f2fs.h fsck.h node.h quotaio.h quotaio_tree.h quotaio_v2.h xattr.h
+noinst_HEADERS = common.h dict.h dqblk_v2.h f2fs.h fsck.h node.h quotaio.h \
+ quotaio_tree.h quotaio_v2.h xattr.h compress.h
include_HEADERS = $(top_srcdir)/include/quota.h
fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c resize.c \
- node.c segment.c dir.c sload.c xattr.c \
+ node.c segment.c dir.c sload.c xattr.c compress.c \
dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c
-fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} \
+ ${liblzo2_LIBS} ${liblz4_LIBS} \
+ $(top_builddir)/lib/libf2fs.la
install-data-hook:
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs
+ ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/f2fslabel
diff --git a/fsck/compress.c b/fsck/compress.c
new file mode 100644
index 0000000..620768d
--- /dev/null
+++ b/fsck/compress.c
@@ -0,0 +1,178 @@
+/**
+ * compress.c
+ *
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add sload compression support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* for config.h for general environment (non-Android) */
+#include "f2fs.h"
+
+#include "compress.h"
+#ifdef HAVE_LIBLZO2
+#include <lzo/lzo1x.h> /* for lzo1x_1_15_compress() */
+#endif
+#ifdef HAVE_LIBLZ4
+#include <lz4.h> /* for LZ4_compress_fast_extState() */
+#endif
+
+/*
+ * macro/constants borrowed from kernel header (GPL-2.0):
+ * include/linux/lzo.h, and include/linux/lz4.h
+ */
+#ifdef HAVE_LIBLZO2
+#define lzo1x_worst_compress(x) ((x) + (x) / 16 + 64 + 3 + 2)
+#define LZO_WORK_SIZE ALIGN_UP(LZO1X_1_15_MEM_COMPRESS, 8)
+#endif
+#ifdef HAVE_LIBLZ4
+#define LZ4_MEMORY_USAGE 14
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#ifndef LZ4_STREAMSIZE
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
+#endif
+#define LZ4_MEM_COMPRESS LZ4_STREAMSIZE
+#define LZ4_ACCELERATION_DEFAULT 1
+#define LZ4_WORK_SIZE ALIGN_UP(LZ4_MEM_COMPRESS, 8)
+#endif
+
+#if defined(HAVE_LIBLZO2) || defined(HAVE_LIBLZ4)
+static void reset_cc(struct compress_ctx *cc)
+{
+ memset(cc->rbuf, 0, cc->cluster_size * F2FS_BLKSIZE);
+ memset(cc->cbuf->cdata, 0, cc->cluster_size * F2FS_BLKSIZE
+ - F2FS_BLKSIZE);
+}
+#endif
+
+#ifdef HAVE_LIBLZO2
+static void lzo_compress_init(struct compress_ctx *cc)
+{
+ size_t size = cc->cluster_size * F2FS_BLKSIZE;
+ size_t alloc = size + lzo1x_worst_compress(size)
+ + COMPRESS_HEADER_SIZE + LZO_WORK_SIZE;
+ cc->private = malloc(alloc);
+ ASSERT(cc->private);
+ cc->rbuf = (char *) cc->private + LZO_WORK_SIZE;
+ cc->cbuf = (struct compress_data *)((char *) cc->rbuf + size);
+}
+
+static int lzo_compress(struct compress_ctx *cc)
+{
+ int ret = lzo1x_1_15_compress(cc->rbuf, cc->rlen, cc->cbuf->cdata,
+ (lzo_uintp)(&cc->clen), cc->private);
+ cc->cbuf->clen = cpu_to_le32(cc->clen);
+ return ret;
+}
+#endif
+
+#ifdef HAVE_LIBLZ4
+static void lz4_compress_init(struct compress_ctx *cc)
+{
+ size_t size = cc->cluster_size * F2FS_BLKSIZE;
+ size_t alloc = size + LZ4_COMPRESSBOUND(size)
+ + COMPRESS_HEADER_SIZE + LZ4_WORK_SIZE;
+ cc->private = malloc(alloc);
+ ASSERT(cc->private);
+ cc->rbuf = (char *) cc->private + LZ4_WORK_SIZE;
+ cc->cbuf = (struct compress_data *)((char *) cc->rbuf + size);
+}
+
+static int lz4_compress(struct compress_ctx *cc)
+{
+ cc->clen = LZ4_compress_fast_extState(cc->private, cc->rbuf,
+ (char *)cc->cbuf->cdata, cc->rlen,
+ cc->rlen - F2FS_BLKSIZE * c.compress.min_blocks,
+ LZ4_ACCELERATION_DEFAULT);
+
+ if (!cc->clen)
+ return 1;
+
+ cc->cbuf->clen = cpu_to_le32(cc->clen);
+ return 0;
+}
+#endif
+
+const char *supported_comp_names[] = {
+ "lzo",
+ "lz4",
+ "",
+};
+
+compress_ops supported_comp_ops[] = {
+#ifdef HAVE_LIBLZO2
+ {lzo_compress_init, lzo_compress, reset_cc},
+#else
+ {NULL, NULL, NULL},
+#endif
+#ifdef HAVE_LIBLZ4
+ {lz4_compress_init, lz4_compress, reset_cc},
+#else
+ {NULL, NULL, NULL},
+#endif
+};
+
+/* linked list */
+typedef struct _ext_t {
+ const char *ext;
+ struct _ext_t *next;
+} ext_t;
+
+static ext_t *extension_list;
+
+static bool ext_found(const char *ext)
+{
+ ext_t *p = extension_list;
+
+ while (p != NULL && strcmp(ext, p->ext))
+ p = p->next;
+ return (p != NULL);
+}
+
+static const char *get_ext(const char *path)
+{
+ char *p = strrchr(path, '.');
+
+ return p == NULL ? path + strlen(path) : p + 1;
+}
+
+static bool ext_do_filter(const char *path)
+{
+ return (ext_found(get_ext(path)) == true) ^
+ (c.compress.filter == COMPR_FILTER_ALLOW);
+}
+
+static void ext_filter_add(const char *ext)
+{
+ ext_t *node;
+
+ ASSERT(ext != NULL);
+ if (ext_found(ext))
+ return; /* ext was already registered */
+ node = malloc(sizeof(ext_t));
+ ASSERT(node != NULL);
+ node->ext = ext;
+ node->next = extension_list;
+ extension_list = node;
+}
+
+static void ext_filter_destroy(void)
+{
+ ext_t *p;
+
+ while (extension_list != NULL) {
+ p = extension_list;
+ extension_list = p->next;
+ free(p);
+ }
+}
+
+filter_ops ext_filter = {
+ .add = ext_filter_add,
+ .destroy = ext_filter_destroy,
+ .filter = ext_do_filter,
+};
diff --git a/fsck/compress.h b/fsck/compress.h
new file mode 100644
index 0000000..917de2d
--- /dev/null
+++ b/fsck/compress.h
@@ -0,0 +1,22 @@
+/**
+ * compress.h
+ *
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add sload compression support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef COMPRESS_H
+#define COMPRESS_H
+
+#include "f2fs_fs.h"
+
+extern const char *supported_comp_names[];
+extern compress_ops supported_comp_ops[];
+extern filter_ops ext_filter;
+
+#endif /* COMPRESS_H */
diff --git a/fsck/defrag.c b/fsck/defrag.c
index 3473637..a630a78 100644
--- a/fsck/defrag.c
+++ b/fsck/defrag.c
@@ -53,7 +53,7 @@ static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
else
update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
- DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
+ DBG(1, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
IS_DATASEG(type) ? "data" : "node",
from, to);
free(raw);
diff --git a/fsck/dir.c b/fsck/dir.c
index 5f4f75e..aeb876d 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -15,6 +15,7 @@
*/
#include "fsck.h"
#include "node.h"
+#include <search.h>
static int room_for_filename(const u8 *bitmap, int slots, int max_slots)
{
@@ -522,6 +523,7 @@ static void init_inode_block(struct f2fs_sb_info *sbi,
node_blk->footer.nid = cpu_to_le32(de->ino);
node_blk->footer.flag = 0;
node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+ set_cold_node(node_blk, S_ISDIR(mode));
if (S_ISDIR(mode)) {
make_empty_dir(sbi, node_blk);
@@ -633,10 +635,43 @@ int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
return 0;
}
+static int cmp_from_devino(const void *a, const void *b) {
+ u64 devino_a = ((struct hardlink_cache_entry*) a)->from_devino;
+ u64 devino_b = ((struct hardlink_cache_entry*) b)->from_devino;
+
+ return (devino_a > devino_b) - (devino_a < devino_b);
+}
+
+struct hardlink_cache_entry *f2fs_search_hardlink(struct f2fs_sb_info *sbi,
+ struct dentry *de)
+{
+ struct hardlink_cache_entry *find_hardlink = NULL;
+ struct hardlink_cache_entry *found_hardlink = NULL;
+ void *search_result;
+
+ /* This might be a hardlink, try to find it in the cache */
+ find_hardlink = calloc(1, sizeof(struct hardlink_cache_entry));
+ find_hardlink->from_devino = de->from_devino;
+
+ search_result = tsearch(find_hardlink, &(sbi->hardlink_cache),
+ cmp_from_devino);
+ ASSERT(search_result != 0);
+
+ found_hardlink = *(struct hardlink_cache_entry**) search_result;
+ ASSERT(find_hardlink->from_devino == found_hardlink->from_devino);
+
+ /* If it was already in the cache, free the entry we just created */
+ if (found_hardlink != find_hardlink)
+ free(find_hardlink);
+
+ return found_hardlink;
+}
+
int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
{
struct f2fs_node *parent, *child;
- struct node_info ni;
+ struct hardlink_cache_entry *found_hardlink = NULL;
+ struct node_info ni, hardlink_ni;
struct f2fs_summary sum;
block_t blkaddr = NULL_ADDR;
int ret;
@@ -648,6 +683,9 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
return -1;
}
+ if (de->from_devino)
+ found_hardlink = f2fs_search_hardlink(sbi, de);
+
parent = calloc(BLOCK_SZ, 1);
ASSERT(parent);
@@ -673,7 +711,26 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
child = calloc(BLOCK_SZ, 1);
ASSERT(child);
- f2fs_alloc_nid(sbi, &de->ino);
+ if (found_hardlink && found_hardlink->to_ino) {
+ /*
+ * If we found this devino in the cache, we're creating a
+ * hard link.
+ */
+ get_node_info(sbi, found_hardlink->to_ino, &hardlink_ni);
+ if (hardlink_ni.blk_addr == NULL_ADDR) {
+ MSG(1, "No original inode for hard link to_ino=%x\n",
+ found_hardlink->to_ino);
+ return -1;
+ }
+
+ /* Use previously-recorded inode */
+ de->ino = found_hardlink->to_ino;
+ blkaddr = hardlink_ni.blk_addr;
+ MSG(1, "Info: Creating \"%s\" as hard link to inode %d\n",
+ de->path, de->ino);
+ } else {
+ f2fs_alloc_nid(sbi, &de->ino);
+ }
init_inode_block(sbi, child, de);
@@ -688,6 +745,30 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
goto free_child_dir;
}
+ if (found_hardlink) {
+ if (!found_hardlink->to_ino) {
+ MSG(2, "Adding inode %d from %s to hardlink cache\n",
+ de->ino, de->path);
+ found_hardlink->to_ino = de->ino;
+ } else {
+ /* Replace child with original block */
+ free(child);
+
+ child = calloc(BLOCK_SZ, 1);
+ ASSERT(child);
+
+ ret = dev_read_block(child, blkaddr);
+ ASSERT(ret >= 0);
+
+ /* Increment links and skip to writing block */
+ child->i.i_links = cpu_to_le32(
+ le32_to_cpu(child->i.i_links) + 1);
+ MSG(2, "Number of links on inode %d is now %d\n",
+ de->ino, le32_to_cpu(child->i.i_links));
+ goto write_child_dir;
+ }
+ }
+
/* write child */
set_summary(&sum, de->ino, 0, ni.version);
ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE, 1);
@@ -696,16 +777,21 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
/* update nat info */
update_nat_blkaddr(sbi, de->ino, de->ino, blkaddr);
+write_child_dir:
ret = dev_write_block(child, blkaddr);
ASSERT(ret >= 0);
update_free_segments(sbi);
MSG(1, "Info: Create %s -> %s\n"
" -- ino=%x, type=%x, mode=%x, uid=%x, "
- "gid=%x, cap=%"PRIx64", size=%lu, pino=%x\n",
+ "gid=%x, cap=%"PRIx64", size=%lu, link=%u "
+ "blocks=%"PRIx64" pino=%x\n",
de->full_path, de->path,
de->ino, de->file_type, de->mode,
- de->uid, de->gid, de->capabilities, de->size, de->pino);
+ de->uid, de->gid, de->capabilities, de->size,
+ le32_to_cpu(child->i.i_links),
+ le64_to_cpu(child->i.i_blocks),
+ de->pino);
free_child_dir:
free(child);
free_parent_dir:
diff --git a/fsck/dump.c b/fsck/dump.c
index 001b7cb..042a2e5 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -278,7 +278,7 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
if (nid == 0) {
*ofs += skip;
- return;
+ goto out;
}
for (i = 0; i < idx; i++, (*ofs)++) {
@@ -297,6 +297,7 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
break;
}
}
+out:
free(node_blk);
}
@@ -424,7 +425,8 @@ static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
return;
}
- if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
+ if ((!S_ISREG(imode) && !S_ISLNK(imode)) ||
+ namelen == 0 || namelen > F2FS_NAME_LEN) {
MSG(force, "Not a regular file or wrong name info\n\n");
return;
}
@@ -481,8 +483,6 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
- dev_read_block(node_blk, ni.blk_addr);
-
DBG(1, "Node ID [0x%x]\n", nid);
DBG(1, "nat_entry.block_addr [0x%x]\n", ni.blk_addr);
DBG(1, "nat_entry.version [0x%x]\n", ni.version);
@@ -493,6 +493,8 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
goto out;
}
+ dev_read_block(node_blk, ni.blk_addr);
+
if (ni.blk_addr == 0x0)
MSG(force, "Invalid nat entry\n\n");
else if (!is_sit_bitmap_set(sbi, ni.blk_addr))
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 76e8272..9c6b0e4 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -221,6 +221,7 @@ struct dentry {
uint64_t capabilities;
nid_t ino;
nid_t pino;
+ u64 from_devino;
};
/* different from dnode_of_data in kernel */
@@ -234,6 +235,12 @@ struct dnode_of_data {
int idirty, ndirty;
};
+struct hardlink_cache_entry {
+ u64 from_devino;
+ nid_t to_ino;
+ int nbuild;
+};
+
struct f2fs_sb_info {
struct f2fs_fsck *fsck;
@@ -276,6 +283,9 @@ struct f2fs_sb_info {
/* true if late_build_segment_manger() is called */
bool seg_manager_done;
+
+ /* keep track of hardlinks so we can recreate them */
+ void *hardlink_cache;
};
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
diff --git a/fsck/fsck.c b/fsck/fsck.c
index c249dfa..80a6d8e 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -144,6 +144,7 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid)
static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
u32 blk_addr)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_summary_block *sum_blk;
struct f2fs_summary *sum_entry;
struct seg_entry * se;
@@ -151,6 +152,9 @@ static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
int need_fix = 0, ret = 0;
int type;
+ if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+ return 0;
+
segno = GET_SEGNO(sbi, blk_addr);
offset = OFFSET_IN_SEG(sbi, blk_addr);
@@ -261,6 +265,7 @@ out:
static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
u32 parent_nid, u16 idx_in_node, u8 version)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_summary_block *sum_blk;
struct f2fs_summary *sum_entry;
struct seg_entry * se;
@@ -268,6 +273,9 @@ static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
int need_fix = 0, ret = 0;
int type;
+ if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+ return 0;
+
segno = GET_SEGNO(sbi, blk_addr);
offset = OFFSET_IN_SEG(sbi, blk_addr);
@@ -790,8 +798,21 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
}
ofs = get_extra_isize(node_blk);
+ if ((node_blk->i.i_flags & cpu_to_le32(F2FS_CASEFOLD_FL)) &&
+ (ftype != F2FS_FT_DIR ||
+ !(c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)))) {
+ ASSERT_MSG("[0x%x] unexpected casefold flag", nid);
+ if (c.fix_on) {
+ FIX_MSG("ino[0x%x] clear casefold flag", nid);
+ node_blk->i.i_flags &= ~cpu_to_le32(F2FS_CASEFOLD_FL);
+ need_fix = 1;
+ }
+ }
+
if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
unsigned int inline_size = MAX_INLINE_DATA(node_blk);
+ if (cur_qtype != -1)
+ qf_szchk_type[cur_qtype] = QF_SZCHK_INLINE;
block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs]);
if (blkaddr != 0) {
@@ -860,6 +881,15 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
}
/* check data blocks in inode */
+ if (cur_qtype != -1) {
+ qf_szchk_type[cur_qtype] = QF_SZCHK_REGFILE;
+ qf_maxsize[cur_qtype] = (ADDRS_PER_INODE(&node_blk->i) +
+ 2 * ADDRS_PER_BLOCK(&node_blk->i) +
+ 2 * ADDRS_PER_BLOCK(&node_blk->i) *
+ NIDS_PER_BLOCK +
+ (u64) ADDRS_PER_BLOCK(&node_blk->i) *
+ NIDS_PER_BLOCK * NIDS_PER_BLOCK) * F2FS_BLKSIZE;
+ }
for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i);
idx++, child.pgofs++) {
block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]);
@@ -884,6 +914,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
file_is_encrypt(&node_blk->i));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
+ if (cur_qtype != -1 && blkaddr != NEW_ADDR)
+ qf_last_blkofs[cur_qtype] = child.pgofs;
} else if (c.fix_on) {
node_blk->i.i_addr[ofs + idx] = 0;
need_fix = 1;
@@ -1126,6 +1158,8 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
file_is_encrypt(inode));
if (!ret) {
*blk_cnt = *blk_cnt + 1;
+ if (cur_qtype != -1 && blkaddr != NEW_ADDR)
+ qf_last_blkofs[cur_qtype] = child->pgofs;
} else if (c.fix_on) {
node_blk->dn.addr[idx] = 0;
need_fix = 1;
@@ -1754,8 +1788,11 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
get_node_info(sbi, ino, &ni);
if (!IS_VALID_NID(sbi, ino) ||
- !IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+ !IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
+ free(orphan_blk);
+ free(new_blk);
return -EINVAL;
+ }
continue;
}
@@ -1794,6 +1831,7 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
u32 blk_cnt = 0;
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ cur_qtype = qtype;
if (sb->qf_ino[qtype] == 0)
continue;
nid_t ino = QUOTA_INO(sb, qtype);
@@ -1811,10 +1849,13 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
}
ret = fsck_chk_node_blk(sbi, NULL, ino,
F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL);
- if (ret)
+ if (ret) {
ASSERT_MSG("wrong quota inode, qtype [%d] ino [0x%x]",
qtype, ino);
+ qf_szchk_type[qtype] = QF_SZCHK_ERR;
+ }
}
+ cur_qtype = -1;
return ret;
}
@@ -1886,11 +1927,12 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi)
if (IS_NODESEG(se->type))
sit_node_blks += se->valid_blocks;
}
- if (fsck->chk.sit_free_segs + sit_valid_segs != TOTAL_SEGS(sbi)) {
+ if (fsck->chk.sit_free_segs + sit_valid_segs !=
+ get_usable_seg_count(sbi)) {
ASSERT_MSG("SIT usage does not match: sit_free_segs %u, "
"sit_valid_segs %u, total_segs %u",
fsck->chk.sit_free_segs, sit_valid_segs,
- TOTAL_SEGS(sbi));
+ get_usable_seg_count(sbi));
return -EINVAL;
}
@@ -2338,10 +2380,15 @@ static int check_curseg_write_pointer(struct f2fs_sb_info *UNUSED(sbi),
int check_curseg_offset(struct f2fs_sb_info *sbi, int type)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, type);
struct seg_entry *se;
int j, nblocks;
+ if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO) &&
+ type != CURSEG_HOT_DATA && type != CURSEG_HOT_NODE)
+ return 0;
+
if ((curseg->next_blkoff >> 3) >= SIT_VBLOCK_MAP_SIZE) {
ASSERT_MSG("Next block offset:%u is invalid, type:%d",
curseg->next_blkoff, type);
@@ -2924,6 +2971,7 @@ void fsck_chk_and_fix_write_pointers(struct f2fs_sb_info *sbi)
int fsck_chk_curseg_info(struct f2fs_sb_info *sbi)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct curseg_info *curseg;
struct seg_entry *se;
struct f2fs_summary_block *sum_blk;
@@ -2934,6 +2982,10 @@ int fsck_chk_curseg_info(struct f2fs_sb_info *sbi)
se = get_seg_entry(sbi, curseg->segno);
sum_blk = curseg->sum_blk;
+ if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ (i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE))
+ continue;
+
if (se->type != i) {
ASSERT_MSG("Incorrect curseg [%d]: segno [0x%x] "
"type(SIT) [%d]", i, curseg->segno,
@@ -3016,7 +3068,10 @@ int fsck_verify(struct f2fs_sb_info *sbi)
}
c.bug_on = 1;
}
-
+ printf("[FSCK] Max image size: %"PRIu64" MB, Free space: %u MB\n",
+ c.max_size >> 20,
+ (sbi->user_block_count - sbi->total_valid_block_count) >>
+ (20 - F2FS_BLKSIZE_BITS));
printf("[FSCK] Unreachable nat entries ");
if (nr_unref_nid == 0x0) {
printf(" [Ok..] [0x%x]\n", nr_unref_nid);
@@ -3115,10 +3170,11 @@ int fsck_verify(struct f2fs_sb_info *sbi)
#ifndef WITH_ANDROID
if (nr_unref_nid && !c.ro) {
char ans[255] = {0};
+ int res;
printf("\nDo you want to restore lost files into ./lost_found/? [Y/N] ");
- ret = scanf("%s", ans);
- ASSERT(ret >= 0);
+ res = scanf("%s", ans);
+ ASSERT(res >= 0);
if (!strcasecmp(ans, "y")) {
for (i = 0; i < fsck->nr_nat_entries; i++) {
if (f2fs_test_bit(i, fsck->nat_area_bitmap))
@@ -3146,6 +3202,8 @@ int fsck_verify(struct f2fs_sb_info *sbi)
is_set_ckpt_flags(cp, CP_QUOTA_NEED_FSCK_FLAG)) {
write_checkpoints(sbi);
}
+ /* to return FSCK_ERROR_CORRECTED */
+ ret = 0;
}
return ret;
}
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 2de6f62..b9dcd5c 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -13,6 +13,17 @@
#include "f2fs.h"
+enum {
+ FSCK_SUCCESS = 0,
+ FSCK_ERROR_CORRECTED = 1 << 0,
+ FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
+ FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
+ FSCK_OPERATIONAL_ERROR = 1 << 3,
+ FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
+ FSCK_USER_CANCELLED = 1 << 5,
+ FSCK_SHARED_LIB_ERROR = 1 << 7,
+};
+
struct quota_ctx;
#define FSCK_UNMATCHED_EXTENT 0x00000001
@@ -224,6 +235,8 @@ 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);
+extern unsigned int get_usable_seg_count(struct f2fs_sb_info *);
+extern bool is_usable_seg(struct f2fs_sb_info *, unsigned int);
/* dump.c */
struct dump_option {
@@ -266,8 +279,19 @@ block_t new_node_block(struct f2fs_sb_info *,
struct dnode_of_data *, unsigned int);
/* segment.c */
+struct quota_file;
+u64 f2fs_quota_size(struct quota_file *);
u64 f2fs_read(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t);
+enum wr_addr_type {
+ WR_NORMAL = 1,
+ WR_COMPRESS_DATA = 2,
+ WR_NULL_ADDR = NULL_ADDR, /* 0 */
+ WR_NEW_ADDR = NEW_ADDR, /* -1U */
+ WR_COMPRESS_ADDR = COMPRESS_ADDR, /* -2U */
+};
u64 f2fs_write(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t);
+u64 f2fs_write_compress_data(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t);
+u64 f2fs_write_addrtag(struct f2fs_sb_info *, nid_t, pgoff_t, unsigned int);
void f2fs_filesize_update(struct f2fs_sb_info *, nid_t, u64);
int get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
@@ -281,6 +305,8 @@ int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
nid_t f2fs_lookup(struct f2fs_sb_info *, struct f2fs_node *, u8 *, int);
int f2fs_add_link(struct f2fs_sb_info *, struct f2fs_node *,
const unsigned char *, int, nid_t, int, block_t, int);
+struct hardlink_cache_entry *f2fs_search_hardlink(struct f2fs_sb_info *sbi,
+ struct dentry *de);
/* xattr.c */
void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *);
diff --git a/fsck/main.c b/fsck/main.c
index 9a1596f..c07be1e 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -13,6 +13,9 @@
* Copyright (c) 2019 Google Inc.
* Robin Hsu <robinhsu@google.com>
* : add cache layer
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add sload compression support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,6 +28,7 @@
#include <getopt.h>
#include <stdbool.h>
#include "quotaio.h"
+#include "compress.h"
struct f2fs_fsck gfsck;
@@ -67,6 +71,7 @@ void fsck_usage()
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -f check/fix entire partition\n");
MSG(0, " -g add default options\n");
+ MSG(0, " -l show superblock/checkpoint\n");
MSG(0, " -O feature1[feature2,feature3,...] e.g. \"encrypt\"\n");
MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n");
MSG(0, " -S sparse_mode\n");
@@ -134,11 +139,31 @@ void sload_usage()
MSG(0, " -S sparse_mode\n");
MSG(0, " -t mount point [prefix of target fs path, default:/]\n");
MSG(0, " -T timestamp\n");
+ MSG(0, " -P preserve owner: user and group\n");
+ MSG(0, " -c enable compression (default allow policy)\n");
+ MSG(0, " ------------ Compression sub-options -----------------\n");
+ MSG(0, " -L <log-of-blocks-per-cluster>, default 2\n");
+ MSG(0, " -a <algorithm> compression algorithm, default LZ4\n");
+ MSG(0, " -x <ext> compress files except for these extensions.\n");
+ MSG(0, " -i <ext> compress files with these extensions only.\n");
+ MSG(0, " * -i or -x: use it many times for multiple extensions.\n");
+ MSG(0, " * -i and -x cannot be used together..\n");
+ MSG(0, " -m <num> min compressed blocks per cluster\n");
+ MSG(0, " -r readonly (IMMUTABLE) for compressed files\n");
+ MSG(0, " ------------------------------------------------------\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -V print the version number and exit\n");
exit(1);
}
+void label_usage()
+{
+ MSG(0, "\nUsage: f2fslabel [options] device [volume-label]\n");
+ MSG(0, "[options]:\n");
+ MSG(0, " -V print the version number and exit\n");
+ exit(1);
+}
+
static int is_digits(char *optarg)
{
unsigned int i;
@@ -161,6 +186,8 @@ static void error_out(char *prog)
resize_usage();
else if (!strcmp("sload.f2fs", prog))
sload_usage();
+ else if (!strcmp("f2fslabel", prog))
+ label_usage();
else
MSG(0, "\nWrong program.\n");
}
@@ -200,7 +227,7 @@ void f2fs_parse_options(int argc, char *argv[])
}
if (!strcmp("fsck.f2fs", prog)) {
- const char *option_string = ":aC:c:m:d:fg:O:p:q:StyV";
+ const char *option_string = ":aC:c:m:d:fg:lO:p:q:StyV";
int opt = 0, val;
char *token;
struct option long_opt[] = {
@@ -247,6 +274,9 @@ void f2fs_parse_options(int argc, char *argv[])
if (!strcmp(optarg, "android"))
c.defset = CONF_ANDROID;
break;
+ case 'l':
+ c.layout = 1;
+ break;
case 'O':
if (parse_feature(feature_table, optarg))
fsck_usage();
@@ -345,6 +375,7 @@ void f2fs_parse_options(int argc, char *argv[])
break;
}
} else if (!strcmp("dump.f2fs", prog)) {
+#ifdef WITH_DUMP
const char *option_string = "d:i:n:s:Sa:b:V";
static struct dump_option dump_opt = {
.nid = 0, /* default root ino */
@@ -426,7 +457,9 @@ void f2fs_parse_options(int argc, char *argv[])
}
c.private = &dump_opt;
+#endif
} else if (!strcmp("defrag.f2fs", prog)) {
+#ifdef WITH_DEFRAG
const char *option_string = "d:s:Sl:t:iV";
c.func = DEFRAG;
@@ -484,8 +517,10 @@ void f2fs_parse_options(int argc, char *argv[])
if (err != NOERROR)
break;
}
+#endif
} else if (!strcmp("resize.f2fs", prog)) {
- const char *option_string = "d:st:iV";
+#ifdef WITH_RESIZE
+ const char *option_string = "d:fst:iV";
c.func = RESIZE;
while ((option = getopt(argc, argv, option_string)) != EOF) {
@@ -501,6 +536,10 @@ void f2fs_parse_options(int argc, char *argv[])
MSG(0, "Info: Debug level = %d\n",
c.dbg_lv);
break;
+ case 'f':
+ c.force = 1;
+ MSG(0, "Info: Force to resize\n");
+ break;
case 's':
c.safe_resize = 1;
break;
@@ -526,8 +565,10 @@ void f2fs_parse_options(int argc, char *argv[])
if (err != NOERROR)
break;
}
+#endif
} else if (!strcmp("sload.f2fs", prog)) {
- const char *option_string = "C:d:f:p:s:St:T:V";
+#ifdef WITH_SLOAD
+ const char *option_string = "cL:a:i:x:m:rC:d:f:p:s:St:T:VP";
#ifdef HAVE_LIBSELINUX
int max_nr_opt = (int)sizeof(c.seopt_file) /
sizeof(c.seopt_file[0]);
@@ -536,8 +577,83 @@ void f2fs_parse_options(int argc, char *argv[])
char *p;
c.func = SLOAD;
+ c.compress.cc.log_cluster_size = 2;
+ c.compress.alg = COMPR_LZ4;
+ c.compress.min_blocks = 1;
+ c.compress.filter_ops = &ext_filter;
while ((option = getopt(argc, argv, option_string)) != EOF) {
+ unsigned int i;
+ int val;
+
switch (option) {
+ case 'c': /* compression support */
+ c.compress.enabled = true;
+ break;
+ case 'L': /* compression: log of blocks-per-cluster */
+ c.compress.required = true;
+ val = atoi(optarg);
+ if (val < MIN_COMPRESS_LOG_SIZE ||
+ val > MAX_COMPRESS_LOG_SIZE) {
+ MSG(0, "\tError: log of blocks per"
+ " cluster must be in the range"
+ " of %d .. %d.\n",
+ MIN_COMPRESS_LOG_SIZE,
+ MAX_COMPRESS_LOG_SIZE);
+ error_out(prog);
+ }
+ c.compress.cc.log_cluster_size = val;
+ break;
+ case 'a': /* compression: choose algorithm */
+ c.compress.required = true;
+ c.compress.alg = MAX_COMPRESS_ALGS;
+ for (i = 0; i < MAX_COMPRESS_ALGS; i++) {
+ if (!strcmp(supported_comp_names[i],
+ optarg)) {
+ c.compress.alg = i;
+ break;
+ }
+ }
+ if (c.compress.alg == MAX_COMPRESS_ALGS) {
+ MSG(0, "\tError: Unknown compression"
+ " algorithm %s\n", optarg);
+ error_out(prog);
+ }
+ break;
+ case 'i': /* compress only these extensions */
+ c.compress.required = true;
+ if (c.compress.filter == COMPR_FILTER_ALLOW) {
+ MSG(0, "\tError: could not mix option"
+ " -i and -x\n");
+ error_out(prog);
+ }
+ c.compress.filter = COMPR_FILTER_DENY;
+ c.compress.filter_ops->add(optarg);
+ break;
+ case 'x': /* compress except for these extensions */
+ c.compress.required = true;
+ if (c.compress.filter == COMPR_FILTER_DENY) {
+ MSG(0, "\tError: could not mix option"
+ " -i and -x\n");
+ error_out(prog);
+ }
+ c.compress.filter = COMPR_FILTER_ALLOW;
+ c.compress.filter_ops->add(optarg);
+ break;
+ case 'm': /* minimum compressed blocks per cluster */
+ c.compress.required = true;
+ val = atoi(optarg);
+ if (val <= 0) {
+ MSG(0, "\tError: minimum compressed"
+ " blocks per cluster must be"
+ " positive.\n");
+ error_out(prog);
+ }
+ c.compress.min_blocks = val;
+ break;
+ case 'r': /* compress file to set IMMUTABLE */
+ c.compress.required = true;
+ c.compress.readonly = true;
+ break;
case 'C':
c.fs_config_file = absolute_path(optarg);
break;
@@ -588,6 +704,9 @@ void f2fs_parse_options(int argc, char *argv[])
case 'V':
show_version(prog);
exit(0);
+ case 'P':
+ c.preserve_perms = 1;
+ break;
default:
err = EUNKNOWN_OPT;
break;
@@ -595,22 +714,79 @@ void f2fs_parse_options(int argc, char *argv[])
if (err != NOERROR)
break;
}
- }
+ if (c.compress.required && !c.compress.enabled) {
+ MSG(0, "\tError: compression sub-options are used"
+ " without the compression enable (-c) option\n"
+ );
+ error_out(prog);
+ }
+ if (err == NOERROR && c.compress.enabled) {
+ c.compress.cc.cluster_size = 1
+ << c.compress.cc.log_cluster_size;
+ if (c.compress.filter == COMPR_FILTER_UNASSIGNED)
+ c.compress.filter = COMPR_FILTER_ALLOW;
+ if (c.compress.min_blocks >=
+ c.compress.cc.cluster_size) {
+ MSG(0, "\tError: minimum reduced blocks by"
+ " compression per cluster must be at"
+ " most one less than blocks per"
+ " cluster, i.e. %d\n",
+ c.compress.cc.cluster_size - 1);
+ error_out(prog);
+ }
+ }
+#endif /* WITH_SLOAD */
+ } else if (!strcmp("f2fslabel", prog)) {
+#ifdef WITH_LABEL
+ const char *option_string = "V";
- add_default_options();
+ c.func = LABEL;
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ switch (option) {
+ case 'V':
+ show_version(prog);
+ exit(0);
+ default:
+ err = EUNKNOWN_OPT;
+ break;
+ }
+ if (err != NOERROR)
+ break;
+ }
- if (optind >= argc) {
- MSG(0, "\tError: Device not specified\n");
- error_out(prog);
+ if (argc > (optind + 2)) { /* unknown argument(s) is(are) passed */
+ optind += 2;
+ err = EUNKNOWN_ARG;
+ } else if (argc == (optind + 2)) { /* change label */
+ c.vol_label = argv[optind + 1];
+ argc--;
+ } else { /* print label */
+ /*
+ * Since vol_label was initialized as "", in order to
+ * distinguish between clear label and print, set
+ * vol_label as NULL for print case
+ */
+ c.vol_label = NULL;
+ }
+#endif /* WITH_LABEL */
}
- c.devices[0].path = strdup(argv[optind]);
- if (argc > (optind + 1)) {
- c.dbg_lv = 0;
- err = EUNKNOWN_ARG;
+ if (err == NOERROR) {
+ add_default_options();
+
+ 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;
+ }
+ if (err == NOERROR)
+ return;
}
- if (err == NOERROR)
- return;
/* print out error */
switch (err) {
@@ -630,7 +806,7 @@ void f2fs_parse_options(int argc, char *argv[])
error_out(prog);
}
-static void do_fsck(struct f2fs_sb_info *sbi)
+static int do_fsck(struct f2fs_sb_info *sbi)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
u32 flag = le32_to_cpu(ckpt->ckpt_flags);
@@ -655,7 +831,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
} else {
MSG(0, "[FSCK] F2FS metadata [Ok..]");
fsck_free(sbi);
- return;
+ return FSCK_SUCCESS;
}
if (!c.ro)
@@ -687,7 +863,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
ret = quota_init_context(sbi);
if (ret) {
ASSERT_MSG("quota_init_context failure: %d", ret);
- return;
+ return FSCK_OPERATIONAL_ERROR;
}
}
fsck_chk_orphan_node(sbi);
@@ -695,10 +871,17 @@ static void do_fsck(struct f2fs_sb_info *sbi)
F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
fsck_chk_quota_files(sbi);
- fsck_verify(sbi);
+ ret = fsck_verify(sbi);
fsck_free(sbi);
+
+ if (!c.bug_on)
+ return FSCK_SUCCESS;
+ if (!ret)
+ return FSCK_ERROR_CORRECTED;
+ return FSCK_ERRORS_LEFT_UNCORRECTED;
}
+#ifdef WITH_DUMP
static void do_dump(struct f2fs_sb_info *sbi)
{
struct dump_option *opt = (struct dump_option *)c.private;
@@ -725,11 +908,18 @@ static void do_dump(struct f2fs_sb_info *sbi)
print_cp_state(flag);
}
+#endif
+#ifdef WITH_DEFRAG
static int do_defrag(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) {
+ MSG(0, "Not support on readonly image.\n");
+ return -1;
+ }
+
if (c.defrag_start > get_sb(block_count))
goto out_range;
if (c.defrag_start < SM_I(sbi)->main_blkaddr)
@@ -774,7 +964,9 @@ out_range:
c.defrag_target);
return -1;
}
+#endif
+#ifdef WITH_RESIZE
static int do_resize(struct f2fs_sb_info *sbi)
{
if (!c.target_sectors)
@@ -788,6 +980,32 @@ static int do_resize(struct f2fs_sb_info *sbi)
return f2fs_resize(sbi);
}
+#endif
+
+#ifdef WITH_SLOAD
+static int init_compr(struct f2fs_sb_info *sbi)
+{
+ if (!c.compress.enabled)
+ return 0;
+
+ if (!(sbi->raw_super->feature
+ & cpu_to_le32(F2FS_FEATURE_COMPRESSION))) {
+ MSG(0, "Error: Compression (-c) was requested "
+ "but the file system is not created "
+ "with such feature.\n");
+ return -1;
+ }
+ if (!supported_comp_ops[c.compress.alg].init) {
+ MSG(0, "Error: The selected compression algorithm is not"
+ " supported\n");
+ return -1;
+ }
+ c.compress.ops = supported_comp_ops + c.compress.alg;
+ c.compress.ops->init(&c.compress.cc);
+ c.compress.ops->reset(&c.compress.cc);
+ c.compress.cc.rlen = c.compress.cc.cluster_size * F2FS_BLKSIZE;
+ return 0;
+}
static int do_sload(struct f2fs_sb_info *sbi)
{
@@ -798,8 +1016,42 @@ static int do_sload(struct f2fs_sb_info *sbi)
if (!c.mount_point)
c.mount_point = "/";
+ if (init_compr(sbi))
+ return -1;
+
return f2fs_sload(sbi);
}
+#endif
+
+#ifdef WITH_LABEL
+static int do_label(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+ if (!c.vol_label) {
+ char label[MAX_VOLUME_NAME];
+
+ utf16_to_utf8(label, sb->volume_name,
+ MAX_VOLUME_NAME, MAX_VOLUME_NAME);
+ MSG(0, "Info: volume label = %s\n", label);
+ return 0;
+ }
+
+ if (strlen(c.vol_label) > MAX_VOLUME_NAME) {
+ ERR_MSG("Label should not exceed %d characters\n", MAX_VOLUME_NAME);
+ return -1;
+ }
+
+ utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
+ MAX_VOLUME_NAME, strlen(c.vol_label));
+
+ update_superblock(sb, SB_MASK_ALL);
+
+ MSG(0, "Info: volume label is changed to %s\n", c.vol_label);
+
+ return 0;
+}
+#endif
#if defined(__APPLE__)
static u64 get_boottime_ns()
@@ -823,7 +1075,7 @@ static u64 get_boottime_ns()
int main(int argc, char **argv)
{
struct f2fs_sb_info *sbi;
- int ret = 0;
+ int ret = 0, ret2;
u64 start = get_boottime_ns();
f2fs_init_configuration();
@@ -833,11 +1085,15 @@ int main(int argc, char **argv)
if (c.func != DUMP && f2fs_devs_are_umounted() < 0) {
if (errno == EBUSY) {
ret = -1;
+ if (c.func == FSCK)
+ ret = FSCK_OPERATIONAL_ERROR;
goto quick_err;
}
if (!c.ro || c.func == DEFRAG) {
MSG(0, "\tError: Not available on mounted device!\n");
ret = -1;
+ if (c.func == FSCK)
+ ret = FSCK_OPERATIONAL_ERROR;
goto quick_err;
}
@@ -854,6 +1110,8 @@ int main(int argc, char **argv)
/* Get device */
if (f2fs_get_device_info() < 0) {
ret = -1;
+ if (c.func == FSCK)
+ ret = FSCK_OPERATIONAL_ERROR;
goto quick_err;
}
@@ -873,7 +1131,7 @@ fsck_again:
switch (c.func) {
case FSCK:
- do_fsck(sbi);
+ ret = do_fsck(sbi);
break;
#ifdef WITH_DUMP
case DUMP:
@@ -909,6 +1167,12 @@ fsck_again:
c.fix_on = 1;
goto fsck_again;
#endif
+#ifdef WITH_LABEL
+ case LABEL:
+ if (do_label(sbi))
+ goto out_err;
+ break;
+#endif
default:
ERR_MSG("Wrong program name\n");
ASSERT(0);
@@ -921,8 +1185,8 @@ fsck_again:
char ans[255] = {0};
retry:
printf("Do you want to fix this partition? [Y/N] ");
- ret = scanf("%s", ans);
- ASSERT(ret >= 0);
+ ret2 = scanf("%s", ans);
+ ASSERT(ret2 >= 0);
if (!strcasecmp(ans, "y"))
c.fix_on = 1;
else if (!strcasecmp(ans, "n"))
@@ -934,12 +1198,18 @@ retry:
goto fsck_again;
}
}
- ret = f2fs_finalize_device();
- if (ret < 0)
- return ret;
+ ret2 = f2fs_finalize_device();
+ if (ret2) {
+ if (c.func == FSCK)
+ return FSCK_OPERATIONAL_ERROR;
+ return ret2;
+ }
+
+ if (c.func == SLOAD)
+ c.compress.filter_ops->destroy();
printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0);
- return 0;
+ return ret;
out_err:
if (sbi->ckpt)
diff --git a/fsck/mkquota.c b/fsck/mkquota.c
index 84f9d3d..c419a0f 100644
--- a/fsck/mkquota.c
+++ b/fsck/mkquota.c
@@ -378,6 +378,7 @@ errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi,
err = quota_file_open(sbi, &qh, qtype, 0);
if (err) {
log_debug("Open quota file failed");
+ *usage_inconsistent = 1;
goto out;
}
diff --git a/fsck/mount.c b/fsck/mount.c
index 6d467c8..de692b6 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -30,6 +30,76 @@
#define ACL_OTHER (0x20)
#endif
+static int get_device_idx(struct f2fs_sb_info *sbi, u_int32_t segno)
+{
+ block_t seg_start_blkaddr;
+ int i;
+
+ seg_start_blkaddr = SM_I(sbi)->main_blkaddr +
+ segno * DEFAULT_BLOCKS_PER_SEGMENT;
+ for (i = 0; i < c.ndevs; i++)
+ if (c.devices[i].start_blkaddr <= seg_start_blkaddr &&
+ c.devices[i].end_blkaddr > seg_start_blkaddr)
+ return i;
+ return 0;
+}
+
+#ifdef HAVE_LINUX_BLKZONED_H
+
+static int get_zone_idx_from_dev(struct f2fs_sb_info *sbi,
+ u_int32_t segno, u_int32_t dev_idx)
+{
+ block_t seg_start_blkaddr = START_BLOCK(sbi, segno);
+
+ return (seg_start_blkaddr - c.devices[dev_idx].start_blkaddr) >>
+ log_base_2(sbi->segs_per_sec * sbi->blocks_per_seg);
+}
+
+bool is_usable_seg(struct f2fs_sb_info *sbi, unsigned int segno)
+{
+ unsigned int secno = segno / sbi->segs_per_sec;
+ block_t seg_start = START_BLOCK(sbi, segno);
+ block_t blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec;
+ unsigned int dev_idx = get_device_idx(sbi, segno);
+ unsigned int zone_idx = get_zone_idx_from_dev(sbi, segno, dev_idx);
+ unsigned int sec_off = SM_I(sbi)->main_blkaddr >>
+ log_base_2(blocks_per_sec);
+
+ if (zone_idx < c.devices[dev_idx].nr_rnd_zones)
+ return true;
+
+ if (c.devices[dev_idx].zoned_model != F2FS_ZONED_HM)
+ return true;
+
+ return seg_start < ((sec_off + secno) * blocks_per_sec) +
+ c.devices[dev_idx].zone_cap_blocks[zone_idx];
+}
+
+unsigned int get_usable_seg_count(struct f2fs_sb_info *sbi)
+{
+ unsigned int i, usable_seg_count = 0;
+
+ for (i = 0; i < TOTAL_SEGS(sbi); i++)
+ if (is_usable_seg(sbi, i))
+ usable_seg_count++;
+
+ return usable_seg_count;
+}
+
+#else
+
+bool is_usable_seg(struct f2fs_sb_info *UNUSED(sbi), unsigned int UNUSED(segno))
+{
+ return true;
+}
+
+unsigned int get_usable_seg_count(struct f2fs_sb_info *sbi)
+{
+ return TOTAL_SEGS(sbi);
+}
+
+#endif
+
u32 get_free_segments(struct f2fs_sb_info *sbi)
{
u32 i, free_segs = 0;
@@ -37,7 +107,8 @@ u32 get_free_segments(struct f2fs_sb_info *sbi)
for (i = 0; i < TOTAL_SEGS(sbi); i++) {
struct seg_entry *se = get_seg_entry(sbi, i);
- if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, i))
+ if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, i) &&
+ is_usable_seg(sbi, i))
free_segs++;
}
return free_segs;
@@ -298,11 +369,16 @@ static void DISP_label(u_int16_t *name)
char buffer[MAX_VOLUME_NAME];
utf16_to_utf8(buffer, name, MAX_VOLUME_NAME, MAX_VOLUME_NAME);
- printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
+ if (c.layout)
+ printf("%-30s %s\n", "Filesystem volume name:", buffer);
+ else
+ printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
}
void print_raw_sb_info(struct f2fs_super_block *sb)
{
+ if (c.layout)
+ goto printout;
if (!c.dbg_lv)
return;
@@ -310,7 +386,7 @@ void print_raw_sb_info(struct f2fs_super_block *sb)
printf("+--------------------------------------------------------+\n");
printf("| Super block |\n");
printf("+--------------------------------------------------------+\n");
-
+printout:
DISP_u32(sb, magic);
DISP_u32(sb, major_ver);
@@ -356,6 +432,8 @@ void print_ckpt_info(struct f2fs_sb_info *sbi)
{
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+ if (c.layout)
+ goto printout;
if (!c.dbg_lv)
return;
@@ -363,7 +441,7 @@ void print_ckpt_info(struct f2fs_sb_info *sbi)
printf("+--------------------------------------------------------+\n");
printf("| Checkpoint |\n");
printf("+--------------------------------------------------------+\n");
-
+printout:
DISP_u64(cp, checkpoint_ver);
DISP_u64(cp, user_block_count);
DISP_u64(cp, valid_block_count);
@@ -490,6 +568,9 @@ void print_sb_state(struct f2fs_super_block *sb)
if (f & cpu_to_le32(F2FS_FEATURE_COMPRESSION)) {
MSG(0, "%s", " compression");
}
+ if (f & cpu_to_le32(F2FS_FEATURE_RO)) {
+ MSG(0, "%s", " ro");
+ }
MSG(0, "\n");
MSG(0, "Info: superblock encrypt level = %d, salt = ",
sb->encryption_level);
@@ -722,7 +803,7 @@ static int verify_sb_chksum(struct f2fs_super_block *sb)
int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
{
unsigned int blocksize;
- unsigned int segment_count, segs_per_sec, secs_per_zone;
+ unsigned int segment_count, segs_per_sec, secs_per_zone, segs_per_zone;
unsigned int total_sections, blocks_per_seg;
if ((get_sb(feature) & F2FS_FEATURE_SB_CHKSUM) &&
@@ -774,6 +855,7 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
segs_per_sec = get_sb(segs_per_sec);
secs_per_zone = get_sb(secs_per_zone);
total_sections = get_sb(section_count);
+ segs_per_zone = segs_per_sec * secs_per_zone;
/* blocks_per_seg should be 512, given the above check */
blocks_per_seg = 1 << get_sb(log_blocks_per_seg);
@@ -784,9 +866,10 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
return -1;
}
- if (total_sections > segment_count ||
+ if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ (total_sections > segment_count ||
total_sections < F2FS_MIN_SEGMENTS ||
- segs_per_sec > segment_count || !segs_per_sec) {
+ segs_per_sec > segment_count || !segs_per_sec)) {
MSG(0, "\tInvalid segment/section count (%u, %u x %u)\n",
segment_count, total_sections, segs_per_sec);
return 1;
@@ -812,7 +895,7 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
dev_segs += le32_to_cpu(sb->devs[i].total_segments);
i++;
}
- if (segment_count != dev_segs) {
+ if (segment_count != dev_segs / segs_per_zone * segs_per_zone) {
MSG(0, "Segment count (%u) mismatch with total segments from devices (%u)",
segment_count, dev_segs);
return 1;
@@ -895,12 +978,13 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr)
c.sb_version, c.version);
if (!c.no_kernel_check &&
memcmp(c.sb_version, c.version, VERSION_LEN)) {
+ c.auto_fix = 0;
+ c.fix_on = 1;
+ }
+ if (c.fix_on) {
memcpy(sbi->raw_super->version,
c.version, VERSION_LEN);
update_superblock(sbi->raw_super, SB_MASK(sb_addr));
-
- c.auto_fix = 0;
- c.fix_on = 1;
}
print_sb_state(sbi->raw_super);
return 0;
@@ -1182,14 +1266,16 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
ovp_segments = get_cp(overprov_segment_count);
reserved_segments = get_cp(rsvd_segment_count);
- if (fsmeta < F2FS_MIN_SEGMENT || ovp_segments == 0 ||
- reserved_segments == 0) {
+ if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ (fsmeta < F2FS_MIN_SEGMENT || ovp_segments == 0 ||
+ reserved_segments == 0)) {
MSG(0, "\tWrong layout: check mkfs.f2fs version\n");
return 1;
}
user_block_count = get_cp(user_block_count);
- segment_count_main = get_sb(segment_count_main);
+ segment_count_main = get_sb(segment_count_main) +
+ (cpu_to_le32(F2FS_FEATURE_RO) ? 1 : 0);
log_blocks_per_seg = get_sb(log_blocks_per_seg);
if (!user_block_count || user_block_count >=
segment_count_main << log_blocks_per_seg) {
@@ -1812,11 +1898,15 @@ static void read_normal_summaries(struct f2fs_sb_info *sbi, int type)
void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr,
struct f2fs_summary *sum)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_summary_block *sum_blk;
u32 segno, offset;
int type, ret;
struct seg_entry *se;
+ if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+ return;
+
segno = GET_SEGNO(sbi, blk_addr);
offset = OFFSET_IN_SEG(sbi, blk_addr);
@@ -1866,9 +1956,9 @@ static int build_curseg(struct f2fs_sb_info *sbi)
SM_I(sbi)->curseg_array = array;
for (i = 0; i < NR_CURSEG_TYPE; i++) {
- array[i].sum_blk = malloc(PAGE_CACHE_SIZE);
+ array[i].sum_blk = calloc(PAGE_CACHE_SIZE, 1);
if (!array[i].sum_blk) {
- MSG(1, "\tError: Malloc failed for build_curseg!!\n");
+ MSG(1, "\tError: Calloc failed for build_curseg!!\n");
goto seg_cleanup;
}
@@ -2351,7 +2441,7 @@ void build_sit_area_bitmap(struct f2fs_sb_info *sbi)
memcpy(ptr, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
ptr += SIT_VBLOCK_MAP_SIZE;
- if (se->valid_blocks == 0x0) {
+ if (se->valid_blocks == 0x0 && is_usable_seg(sbi, segno)) {
if (le32_to_cpu(sbi->ckpt->cur_node_segno[0]) == segno ||
le32_to_cpu(sbi->ckpt->cur_data_segno[0]) == segno ||
le32_to_cpu(sbi->ckpt->cur_node_segno[1]) == segno ||
@@ -2411,7 +2501,7 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi)
se->valid_blocks = valid_blocks;
type = se->type;
if (type >= NO_CHECK_TYPE) {
- ASSERT_MSG("Invalide type and valid blocks=%x,%x",
+ ASSERT_MSG("Invalid type and valid blocks=%x,%x",
segno, valid_blocks);
type = 0;
}
@@ -2651,18 +2741,17 @@ int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left,
bitmap = get_seg_bitmap(sbi, se);
type = get_seg_type(sbi, se);
- if (vblocks == sbi->blocks_per_seg ||
- IS_CUR_SEGNO(sbi, segno)) {
- *to = left ? START_BLOCK(sbi, segno) - 1:
- START_BLOCK(sbi, segno + 1);
- continue;
- }
-
- if (vblocks == 0 && not_enough) {
+ if (vblocks == sbi->blocks_per_seg) {
+next_segment:
*to = left ? START_BLOCK(sbi, segno) - 1:
START_BLOCK(sbi, segno + 1);
continue;
}
+ if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ IS_CUR_SEGNO(sbi, segno))
+ goto next_segment;
+ if (vblocks == 0 && not_enough)
+ goto next_segment;
if (vblocks == 0 && !(segno % sbi->segs_per_sec)) {
struct seg_entry *se2;
@@ -2693,17 +2782,24 @@ int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left,
static void move_one_curseg_info(struct f2fs_sb_info *sbi, u64 from, int left,
int i)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, i);
struct f2fs_summary_block buf;
u32 old_segno;
u64 ssa_blk, to;
int ret;
+ if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))) {
+ if (i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE)
+ return;
+ goto bypass_ssa;
+ }
+
/* update original SSA too */
ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
ret = dev_write_block(curseg->sum_blk, ssa_blk);
ASSERT(ret >= 0);
-
+bypass_ssa:
to = from;
ret = find_next_free_block(sbi, &to, left, i,
c.zoned_model == F2FS_ZONED_HM);
@@ -2942,10 +3038,12 @@ void write_checkpoint(struct f2fs_sb_info *sbi)
ret = dev_write_block(curseg->sum_blk, cp_blk_no++);
ASSERT(ret >= 0);
- /* update original SSA too */
- ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
- ret = dev_write_block(curseg->sum_blk, ssa_blk);
- ASSERT(ret >= 0);
+ if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))) {
+ /* update original SSA too */
+ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+ ret = dev_write_block(curseg->sum_blk, ssa_blk);
+ ASSERT(ret >= 0);
+ }
}
/* Write nat bits */
@@ -3462,6 +3560,8 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
if (get_cp(ckpt_flags) & CP_QUOTA_NEED_FSCK_FLAG)
c.fix_on = 1;
}
+ if (c.layout)
+ return 1;
if (tune_sb_features(sbi))
return -1;
diff --git a/fsck/node.c b/fsck/node.c
index 229a99c..c7988cb 100644
--- a/fsck/node.c
+++ b/fsck/node.c
@@ -61,6 +61,7 @@ void set_data_blkaddr(struct dnode_of_data *dn)
block_t new_node_block(struct f2fs_sb_info *sbi,
struct dnode_of_data *dn, unsigned int ofs)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_node *f2fs_inode;
struct f2fs_node *node_blk;
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -79,6 +80,7 @@ block_t new_node_block(struct f2fs_sb_info *sbi,
node_blk->footer.ino = f2fs_inode->footer.ino;
node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+ set_cold_node(node_blk, S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode)));
type = CURSEG_COLD_NODE;
if (IS_DNODE(node_blk)) {
@@ -88,6 +90,10 @@ block_t new_node_block(struct f2fs_sb_info *sbi,
type = CURSEG_WARM_NODE;
}
+ if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ type != CURSEG_HOT_NODE)
+ type = CURSEG_HOT_NODE;
+
get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, 0, ni.version);
ret = reserve_new_block(sbi, &blkaddr, &sum, type, !ofs);
diff --git a/fsck/node.h b/fsck/node.h
index 6bce1fb..99139b1 100644
--- a/fsck/node.h
+++ b/fsck/node.h
@@ -161,6 +161,17 @@ static inline int is_node(struct f2fs_node *node_blk, int type)
return le32_to_cpu(node_blk->footer.flag) & (1 << type);
}
+static inline void set_cold_node(struct f2fs_node *rn, bool is_dir)
+{
+ unsigned int flag = le32_to_cpu(rn->footer.flag);
+
+ if (is_dir)
+ flag &= ~(0x1 << COLD_BIT_SHIFT);
+ else
+ flag |= (0x1 << COLD_BIT_SHIFT);
+ rn->footer.flag = cpu_to_le32(flag);
+}
+
#define is_fsync_dnode(node_blk) is_node(node_blk, FSYNC_BIT_SHIFT)
#define is_dent_dnode(node_blk) is_node(node_blk, DENT_BIT_SHIFT)
diff --git a/fsck/quotaio.c b/fsck/quotaio.c
index cc517bd..51abbb7 100644
--- a/fsck/quotaio.c
+++ b/fsck/quotaio.c
@@ -33,6 +33,14 @@ struct disk_dqheader {
__le32 dqh_version;
} __attribute__ ((packed));
+int cur_qtype = -1;
+u32 qf_last_blkofs[MAXQUOTAS] = {0, 0, 0};
+enum qf_szchk_type_t qf_szchk_type[MAXQUOTAS] =
+{
+ QF_SZCHK_NONE, QF_SZCHK_NONE, QF_SZCHK_NONE
+};
+u64 qf_maxsize[MAXQUOTAS];
+
/**
* Convert type of quota to written representation
*/
@@ -140,7 +148,7 @@ errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
goto errout;
}
- if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
+ if (h->qh_ops->init_io && (h->qh_ops->init_io(h, qtype) < 0)) {
log_err("qh_ops->init_io failed");
err = EIO;
goto errout;
diff --git a/fsck/quotaio.h b/fsck/quotaio.h
index 8087309..999e800 100644
--- a/fsck/quotaio.h
+++ b/fsck/quotaio.h
@@ -46,6 +46,17 @@ enum quota_type {
#error "cannot have more than 32 quota types to fit in qtype_bits"
#endif
+enum qf_szchk_type_t {
+ QF_SZCHK_NONE,
+ QF_SZCHK_ERR,
+ QF_SZCHK_INLINE,
+ QF_SZCHK_REGFILE,
+};
+
+extern int cur_qtype;
+extern u32 qf_last_blkofs[];
+extern enum qf_szchk_type_t qf_szchk_type[];
+extern u64 qf_maxsize[];
#define QUOTA_USR_BIT (1 << USRQUOTA)
#define QUOTA_GRP_BIT (1 << GRPQUOTA)
@@ -154,7 +165,7 @@ struct quotafile_ops {
/* Check whether quotafile is in our format */
int (*check_file) (struct quota_handle *h, int type);
/* Open quotafile */
- int (*init_io) (struct quota_handle *h);
+ int (*init_io) (struct quota_handle *h, enum quota_type qtype);
/* Create new quotafile */
int (*new_io) (struct quota_handle *h);
/* Write all changes and close quotafile */
diff --git a/fsck/quotaio_tree.c b/fsck/quotaio_tree.c
index de25a60..c203400 100644
--- a/fsck/quotaio_tree.c
+++ b/fsck/quotaio_tree.c
@@ -568,7 +568,7 @@ static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
int entries, i;
if (!buf)
- return 0;
+ return -1;
set_bit(bitmap, blk);
read_blk(dquot->dq_h, blk, buf);
@@ -593,9 +593,7 @@ static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
static int check_reference(struct quota_handle *h, unsigned int blk)
{
if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
- log_err("Illegal reference (%u >= %u) in %s quota file. "
- "Quota file is probably corrupted.\n"
- "Please run fsck (8) to fix it.",
+ log_err("Illegal reference (%u >= %u) in %s quota file",
blk,
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
quota_type2name(h->qh_type));
@@ -627,9 +625,13 @@ static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
break;
if (depth == QT_TREEDEPTH - 1) {
- if (!get_bit(bitmap, blk))
- *entries += report_block(dquot, blk, bitmap,
+ if (!get_bit(bitmap, blk)) {
+ int num_entry = report_block(dquot, blk, bitmap,
process_dquot, data);
+ if (num_entry < 0)
+ break;
+ *entries += num_entry;
+ }
} else {
if (report_tree(dquot, blk, depth + 1, bitmap, entries,
process_dquot, data))
diff --git a/fsck/quotaio_v2.c b/fsck/quotaio_v2.c
index 1404332..9353f85 100644
--- a/fsck/quotaio_v2.c
+++ b/fsck/quotaio_v2.c
@@ -20,7 +20,7 @@
#include "quotaio_tree.h"
static int v2_check_file(struct quota_handle *h, int type);
-static int v2_init_io(struct quota_handle *h);
+static int v2_init_io(struct quota_handle *h, enum quota_type qtype);
static int v2_new_io(struct quota_handle *h);
static int v2_write_info(struct quota_handle *h);
static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id);
@@ -170,19 +170,64 @@ static int v2_check_file(struct quota_handle *h, int type)
/*
* Open quotafile
*/
-static int v2_init_io(struct quota_handle *h)
+static int v2_init_io(struct quota_handle *h, enum quota_type qtype)
{
struct v2_disk_dqinfo ddqinfo;
+ struct v2_mem_dqinfo *info;
+ u64 filesize;
+ struct quota_file *qf = &h->qh_qf;
+ u32 last_blkofs = qf_last_blkofs[qtype];
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
sizeof(struct v2r1_disk_dqblk);
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
/* Read information about quotafile */
- if (h->read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
- sizeof(ddqinfo)) != sizeof(ddqinfo))
+ if (h->read(qf, V2_DQINFOOFF, &ddqinfo,
+ sizeof(ddqinfo)) != sizeof(ddqinfo))
return -1;
v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
+
+ /* Check to make sure quota file info is sane */
+ info = &h->qh_info.u.v2_mdqi;
+ filesize = qf->filesize = f2fs_quota_size(qf);
+ if (qf_szchk_type[qtype] == QF_SZCHK_REGFILE &&
+ ((filesize + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS <
+ last_blkofs + 1 || filesize > qf_maxsize[qtype])) {
+ /*
+ * reqular: qf_szchk is now the last block index,
+ * including the hole's index
+ */
+ log_err("Quota inode %u corrupted: file size %" PRIu64
+ " does not match page offset %" PRIu32,
+ h->qh_qf.ino,
+ filesize,
+ last_blkofs);
+ filesize = (last_blkofs + 1) << F2FS_BLKSIZE_BITS;
+ f2fs_filesize_update(qf->sbi, qf->ino, filesize);
+ }
+
+ if ((info->dqi_qtree.dqi_blocks >
+ (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
+ log_err("Quota inode %u corrupted: file size %" PRId64 "; "
+ "dqi_blocks %u", h->qh_qf.ino,
+ filesize, info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
+ if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
+ log_err("Quota inode %u corrupted: free_blk %u;"
+ " dqi_blocks %u",
+ h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
+ info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
+ if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
+ log_err("Quota inode %u corrupted: free_entry %u; "
+ "dqi_blocks %u", h->qh_qf.ino,
+ info->dqi_qtree.dqi_free_entry,
+ info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
return 0;
}
diff --git a/fsck/resize.c b/fsck/resize.c
index 46b1cfb..78d578e 100644
--- a/fsck/resize.c
+++ b/fsck/resize.c
@@ -383,6 +383,7 @@ static void migrate_nat(struct f2fs_sb_info *sbi,
ASSERT(ret >= 0);
DBG(3, "Write NAT: %lx\n", block_addr);
}
+ free(nat_block);
DBG(0, "Info: Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n",
old_nat_blkaddr, new_nat_blkaddr);
}
@@ -526,6 +527,11 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
(unsigned char *)cp);
+ if (c.safe_resize)
+ memcpy((void *)new_cp + CP_BITMAP_OFFSET,
+ (void *)cp + CP_BITMAP_OFFSET,
+ F2FS_BLKSIZE - CP_BITMAP_OFFSET);
+
new_cp->checkpoint_ver = cpu_to_le64(cp_ver + 1);
crc = f2fs_checkpoint_chksum(new_cp);
@@ -645,8 +651,11 @@ static int f2fs_resize_shrink(struct f2fs_sb_info *sbi)
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_super_block new_sb_raw;
struct f2fs_super_block *new_sb = &new_sb_raw;
+ struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
block_t old_end_blkaddr, old_main_blkaddr;
block_t new_end_blkaddr, new_main_blkaddr, tmp_end_blkaddr;
+ block_t user_block_count;
+ unsigned int overprov_segment_count;
unsigned int offset;
int err = -1;
@@ -657,6 +666,17 @@ static int f2fs_resize_shrink(struct f2fs_sb_info *sbi)
if (get_new_sb(new_sb))
return -1;
+ overprov_segment_count = (get_newsb(segment_count_main) -
+ c.new_reserved_segments) *
+ c.new_overprovision / 100;
+ overprov_segment_count += c.new_reserved_segments;
+
+ user_block_count = (get_newsb(segment_count_main) -
+ overprov_segment_count) * c.blks_per_seg;
+
+ if (get_cp(valid_block_count) > user_block_count)
+ return -1;
+
/* check nat availability */
if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
err = shrink_nats(sbi, new_sb);
@@ -714,8 +734,9 @@ int f2fs_resize(struct f2fs_sb_info *sbi)
} else {
return f2fs_resize_shrink(sbi);
}
- else if ((c.target_sectors * c.sector_size >>
- get_sb(log_blocksize)) > get_sb(block_count))
+ else if (((c.target_sectors * c.sector_size >>
+ get_sb(log_blocksize)) > get_sb(block_count)) ||
+ c.force)
return f2fs_resize_grow(sbi);
else {
MSG(0, "Nothing to resize.\n");
diff --git a/fsck/segment.c b/fsck/segment.c
index b7cf245..0156690 100644
--- a/fsck/segment.c
+++ b/fsck/segment.c
@@ -8,6 +8,9 @@
* Hou Pengyang <houpengyang@huawei.com>
* Liu Shuoran <liushuoran@huawei.com>
* Jaegeuk Kim <jaegeuk@kernel.org>
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add sload compression support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -15,6 +18,7 @@
*/
#include "fsck.h"
#include "node.h"
+#include "quotaio.h"
int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
struct f2fs_summary *sum, int type, bool is_inode)
@@ -100,16 +104,23 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
int new_data_block(struct f2fs_sb_info *sbi, void *block,
struct dnode_of_data *dn, int type)
{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_summary sum;
struct node_info ni;
unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
int ret;
+ if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ type != CURSEG_HOT_DATA)
+ type = CURSEG_HOT_DATA;
+
ASSERT(dn->node_blk);
memset(block, 0, BLOCK_SZ);
get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
+
+ dn->data_blkaddr = blkaddr;
ret = reserve_new_block(sbi, &dn->data_blkaddr, &sum, type, 0);
if (ret) {
c.alloc_failed = 1;
@@ -124,6 +135,25 @@ int new_data_block(struct f2fs_sb_info *sbi, void *block,
return 0;
}
+u64 f2fs_quota_size(struct quota_file *qf)
+{
+ struct node_info ni;
+ struct f2fs_node *inode;
+ u64 filesize;
+
+ inode = (struct f2fs_node *) calloc(BLOCK_SZ, 1);
+ ASSERT(inode);
+
+ /* Read inode */
+ get_node_info(qf->sbi, qf->ino, &ni);
+ ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+ ASSERT(S_ISREG(le16_to_cpu(inode->i.i_mode)));
+
+ filesize = le64_to_cpu(inode->i.i_size);
+ free(inode);
+ return filesize;
+}
+
u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
u64 count, pgoff_t offset)
{
@@ -208,8 +238,14 @@ u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
return read_count;
}
-u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
- u64 count, pgoff_t offset)
+/*
+ * Do not call this function directly. Instead, call one of the following:
+ * u64 f2fs_write();
+ * u64 f2fs_write_compress_data();
+ * u64 f2fs_write_addrtag();
+ */
+static u64 f2fs_write_ex(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
+ u64 count, pgoff_t offset, enum wr_addr_type addr_type)
{
struct dnode_of_data dn;
struct node_info ni;
@@ -223,6 +259,19 @@ u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
void* index_node = NULL;
int idirty = 0;
int err;
+ bool has_data = (addr_type == WR_NORMAL
+ || addr_type == WR_COMPRESS_DATA);
+
+ if (count == 0)
+ return 0;
+
+ /*
+ * Enforce calling from f2fs_write(), f2fs_write_compress_data(),
+ * and f2fs_write_addrtag(). Beside, check if is properly called.
+ */
+ ASSERT((!has_data && buffer == NULL) || (has_data && buffer != NULL));
+ if (addr_type != WR_NORMAL)
+ ASSERT(offset % F2FS_BLKSIZE == 0); /* block boundary only */
/* Memory allocation for block buffer and inode. */
blk_buffer = calloc(BLOCK_SZ, 2);
@@ -245,15 +294,26 @@ u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
if (err)
break;
idirty |= dn.idirty;
- if (index_node)
- free(index_node);
+ free(index_node);
index_node = (dn.node_blk == dn.inode_blk) ?
- NULL : dn.node_blk;
+ NULL : dn.node_blk;
remained_blkentries = ADDRS_PER_PAGE(sbi,
- dn.node_blk, dn.inode_blk);
+ dn.node_blk, dn.inode_blk) -
+ dn.ofs_in_node;
}
ASSERT(remained_blkentries > 0);
+ if (!has_data) {
+ dn.data_blkaddr = addr_type;
+ set_data_blkaddr(&dn);
+ idirty |= dn.idirty;
+ if (dn.ndirty)
+ ASSERT(dev_write_block(dn.node_blk,
+ dn.node_blkaddr) >= 0);
+ written_count = 0;
+ break;
+ }
+
blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
err = new_data_block(sbi, blk_buffer,
@@ -261,6 +321,7 @@ u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
if (err)
break;
blkaddr = dn.data_blkaddr;
+ idirty |= dn.idirty;
}
off_in_blk = offset % BLOCK_SZ;
@@ -285,9 +346,10 @@ u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
dn.ofs_in_node++;
if ((--remained_blkentries == 0 || count == 0) && (dn.ndirty))
- ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr) >= 0);
+ ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr)
+ >= 0);
}
- if (offset > le64_to_cpu(inode->i.i_size)) {
+ if (addr_type == WR_NORMAL && offset > le64_to_cpu(inode->i.i_size)) {
inode->i.i_size = cpu_to_le64(offset);
idirty = 1;
}
@@ -295,13 +357,33 @@ u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
ASSERT(inode == dn.inode_blk);
ASSERT(write_inode(inode, ni.blk_addr) >= 0);
}
- if (index_node)
- free(index_node);
+
+ free(index_node);
free(blk_buffer);
return written_count;
}
+u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
+ u64 count, pgoff_t offset)
+{
+ return f2fs_write_ex(sbi, ino, buffer, count, offset, WR_NORMAL);
+}
+
+u64 f2fs_write_compress_data(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
+ u64 count, pgoff_t offset)
+{
+ return f2fs_write_ex(sbi, ino, buffer, count, offset, WR_COMPRESS_DATA);
+}
+
+u64 f2fs_write_addrtag(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset,
+ unsigned int addrtag)
+{
+ ASSERT(addrtag == COMPRESS_ADDR || addrtag == NEW_ADDR
+ || addrtag == NULL_ADDR);
+ return f2fs_write_ex(sbi, ino, NULL, F2FS_BLKSIZE, offset, addrtag);
+}
+
/* This function updates only inode->i.i_size */
void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize)
{
@@ -322,15 +404,74 @@ void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize)
free(inode);
}
+#define MAX_BULKR_RETRY 5
+int bulkread(int fd, void *rbuf, size_t rsize, bool *eof)
+{
+ int n = 0;
+ int retry = MAX_BULKR_RETRY;
+ int cur;
+
+ if (!rsize)
+ return 0;
+
+ if (eof != NULL)
+ *eof = false;
+ while (rsize && (cur = read(fd, rbuf, rsize)) != 0) {
+ if (cur == -1) {
+ if (errno == EINTR && retry--)
+ continue;
+ return -1;
+ }
+ retry = MAX_BULKR_RETRY;
+
+ rsize -= cur;
+ n += cur;
+ }
+ if (eof != NULL)
+ *eof = (cur == 0);
+ return n;
+}
+
+u64 f2fs_fix_mutable(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset,
+ unsigned int compressed)
+{
+ unsigned int i;
+ u64 wlen;
+
+ if (c.compress.readonly)
+ return 0;
+
+ for (i = 0; i < compressed - 1; i++) {
+ wlen = f2fs_write_addrtag(sbi, ino,
+ offset + (i << F2FS_BLKSIZE_BITS), NEW_ADDR);
+ if (wlen)
+ return wlen;
+ }
+ return 0;
+}
+
int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
{
int fd, n;
pgoff_t off = 0;
u8 buffer[BLOCK_SZ];
+ struct node_info ni;
+ struct f2fs_node *node_blk;
if (de->ino == 0)
return -1;
+ if (de->from_devino) {
+ struct hardlink_cache_entry *found_hardlink;
+
+ found_hardlink = f2fs_search_hardlink(sbi, de);
+ if (found_hardlink && found_hardlink->to_ino &&
+ found_hardlink->nbuild)
+ return 0;
+
+ found_hardlink->nbuild++;
+ }
+
fd = open(de->full_path, O_RDONLY);
if (fd < 0) {
MSG(0, "Skip: Fail to open %s\n", de->full_path);
@@ -339,8 +480,6 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
/* inline_data support */
if (de->size <= DEF_MAX_INLINE_DATA) {
- struct node_info ni;
- struct f2fs_node *node_blk;
int ret;
get_node_info(sbi, de->ino, &ni);
@@ -365,6 +504,86 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
node_blk->i.i_size = cpu_to_le64(de->size);
ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
free(node_blk);
+#ifdef WITH_SLOAD
+ } else if (c.func == SLOAD && c.compress.enabled &&
+ c.compress.filter_ops->filter(de->full_path)) {
+ bool eof = false;
+ u8 *rbuf = c.compress.cc.rbuf;
+ unsigned int cblocks = 0;
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ /* read inode */
+ get_node_info(sbi, de->ino, &ni);
+ ASSERT(dev_read_block(node_blk, ni.blk_addr) >= 0);
+ /* update inode meta */
+ node_blk->i.i_compress_algrithm = c.compress.alg;
+ node_blk->i.i_log_cluster_size =
+ c.compress.cc.log_cluster_size;
+ node_blk->i.i_flags = cpu_to_le32(F2FS_COMPR_FL);
+ if (c.compress.readonly)
+ node_blk->i.i_inline |= F2FS_COMPRESS_RELEASED;
+ ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
+
+ while (!eof && (n = bulkread(fd, rbuf, c.compress.cc.rlen,
+ &eof)) > 0) {
+ int ret = c.compress.ops->compress(&c.compress.cc);
+ u64 wlen;
+ u32 csize = ALIGN_UP(c.compress.cc.clen +
+ COMPRESS_HEADER_SIZE, BLOCK_SZ);
+ unsigned int cur_cblk;
+
+ if (ret || n < c.compress.cc.rlen ||
+ n < (int)(csize + BLOCK_SZ *
+ c.compress.min_blocks)) {
+ wlen = f2fs_write(sbi, de->ino, rbuf, n, off);
+ ASSERT((int)wlen == n);
+ } else {
+ wlen = f2fs_write_addrtag(sbi, de->ino, off,
+ WR_COMPRESS_ADDR);
+ ASSERT(!wlen);
+ wlen = f2fs_write_compress_data(sbi, de->ino,
+ (u8 *)c.compress.cc.cbuf,
+ csize, off + BLOCK_SZ);
+ ASSERT(wlen == csize);
+ c.compress.ops->reset(&c.compress.cc);
+ cur_cblk = (c.compress.cc.rlen - csize) /
+ BLOCK_SZ;
+ cblocks += cur_cblk;
+ wlen = f2fs_fix_mutable(sbi, de->ino,
+ off + BLOCK_SZ + csize,
+ cur_cblk);
+ ASSERT(!wlen);
+ }
+ off += n;
+ }
+ if (n == -1) {
+ fprintf(stderr, "Load file '%s' failed: ",
+ de->full_path);
+ perror(NULL);
+ }
+ /* read inode */
+ get_node_info(sbi, de->ino, &ni);
+ ASSERT(dev_read_block(node_blk, ni.blk_addr) >= 0);
+ /* update inode meta */
+ node_blk->i.i_size = cpu_to_le64(off);
+ if (!c.compress.readonly) {
+ node_blk->i.i_compr_blocks = cpu_to_le64(cblocks);
+ node_blk->i.i_blocks += cpu_to_le64(cblocks);
+ }
+ ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
+ free(node_blk);
+
+ if (!c.compress.readonly) {
+ sbi->total_valid_block_count += cblocks;
+ if (sbi->total_valid_block_count >=
+ sbi->user_block_count) {
+ ERR_MSG("Not enough space\n");
+ ASSERT(0);
+ }
+ }
+#endif
} else {
while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
f2fs_write(sbi, de->ino, buffer, n, off);
diff --git a/fsck/sload.c b/fsck/sload.c
index c064f7f..6929023 100644
--- a/fsck/sload.c
+++ b/fsck/sload.c
@@ -104,10 +104,18 @@ static int set_perms_and_caps(struct dentry *de)
uint64_t capabilities = 0;
unsigned int uid = 0, gid = 0, imode = 0;
char *mnt_path = NULL;
+ char *mount_path = c.mount_point;
- if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) {
+ /*
+ * de->path already has "/" in the beginning of it.
+ * Need to remove "/" when c.mount_point is "/", not to add it twice.
+ */
+ if (strlen(c.mount_point) == 1 && c.mount_point[0] == '/')
+ mount_path = "";
+
+ if (asprintf(&mnt_path, "%s%s", mount_path, de->path) <= 0) {
ERR_MSG("cannot allocate mount path for %s%s\n",
- c.mount_point, de->path);
+ mount_path, de->path);
return -ENOMEM;
}
@@ -140,6 +148,15 @@ static void set_inode_metadata(struct dentry *de)
}
if (S_ISREG(stat.st_mode)) {
+ if (stat.st_nlink > 1) {
+ /*
+ * This file might have multiple links to it, so remember
+ * device and inode.
+ */
+ de->from_devino = stat.st_dev;
+ de->from_devino <<= 32;
+ de->from_devino |= stat.st_ino;
+ }
de->file_type = F2FS_FT_REG_FILE;
} else if (S_ISDIR(stat.st_mode)) {
de->file_type = F2FS_FT_DIR;
@@ -170,6 +187,11 @@ static void set_inode_metadata(struct dentry *de)
else
de->mtime = c.fixed_time;
+ if (c.preserve_perms) {
+ de->uid = stat.st_uid;
+ de->gid = stat.st_gid;
+ }
+
set_perms_and_caps(de);
}
@@ -313,6 +335,9 @@ int f2fs_sload(struct f2fs_sb_info *sbi)
{
int ret = 0;
+ /* this requires for the below sanity checks */
+ fsck_init(sbi);
+
ret = configure_files();
if (ret) {
ERR_MSG("Failed to configure files\n");
@@ -322,6 +347,9 @@ int f2fs_sload(struct f2fs_sb_info *sbi)
/* flush NAT/SIT journal entries */
flush_journal_entries(sbi);
+ /* initialize empty hardlink cache */
+ sbi->hardlink_cache = 0;
+
ret = build_directory(sbi, c.from_dir, "/",
c.target_out_dir, F2FS_ROOT_INO(sbi));
if (ret) {
diff --git a/fsck/xattr.c b/fsck/xattr.c
index e9dcb52..f0c5343 100644
--- a/fsck/xattr.c
+++ b/fsck/xattr.c
@@ -24,7 +24,7 @@ void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
u64 inline_size = inline_xattr_size(&inode->i);
nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
- if (xnid) {
+ if (c.func == FSCK && xnid) {
struct f2fs_node *node_blk = NULL;
struct node_info ni;
int ret;
diff --git a/include/android_config.h b/include/android_config.h
index 0613400..cae1669 100644
--- a/include/android_config.h
+++ b/include/android_config.h
@@ -7,6 +7,8 @@
#define HAVE_POSIX_ACL_H 1
#define HAVE_LINUX_TYPES_H 1
#define HAVE_LINUX_XATTR_H 1
+#define HAVE_LINUX_FS_H 1
+#define HAVE_LINUX_FIEMAP_H 1
#define HAVE_MNTENT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRING_H 1
@@ -27,6 +29,7 @@
#define HAVE_LSEEK64 1
#define HAVE_MEMSET 1
#define HAVE_SETMNTENT 1
+#define HAVE_LIBLZ4 1
#ifdef WITH_SLOAD
#define HAVE_LIBSELINUX 1
@@ -53,6 +56,7 @@
#define HAVE_GETMNTENT 1
#define HAVE_LLSEEK 1
#define HAVE_MEMSET 1
+#define HAVE_LIBLZ4 1
#ifdef WITH_SLOAD
#define HAVE_LIBSELINUX 1
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 709bfd8..8969ae2 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -5,6 +5,9 @@
* http://www.samsung.com/
* Copyright (c) 2019 Google Inc.
* http://www.google.com/
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add sload compression support
*
* Dual licensed under the GPL or LGPL version 2 licenses.
*
@@ -32,6 +35,7 @@
#define WITH_DEFRAG
#define WITH_RESIZE
#define WITH_SLOAD
+#define WITH_LABEL
#endif
#include <inttypes.h>
@@ -68,6 +72,10 @@ typedef uint16_t u_int16_t;
typedef uint8_t u_int8_t;
#endif
+/* codes from kernel's f2fs.h, GPL-v2.0 */
+#define MIN_COMPRESS_LOG_SIZE 2
+#define MAX_COMPRESS_LOG_SIZE 8
+
typedef u_int64_t u64;
typedef u_int32_t u32;
typedef u_int16_t u16;
@@ -93,6 +101,31 @@ typedef u32 __be32;
typedef u64 __be64;
#endif
+/*
+ * code borrowed from kernel f2fs dirver: f2fs.h, GPL-2.0
+ * : definitions of COMPRESS_DATA_RESERVED_SIZE,
+ * struct compress_data, COMPRESS_HEADER_SIZE,
+ * and struct compress_ctx
+ */
+#define COMPRESS_DATA_RESERVED_SIZE 4
+struct compress_data {
+ __le32 clen; /* compressed data size */
+ __le32 chksum; /* checksum of compressed data */
+ __le32 reserved[COMPRESS_DATA_RESERVED_SIZE]; /* reserved */
+ u8 cdata[]; /* compressed data */
+};
+#define COMPRESS_HEADER_SIZE (sizeof(struct compress_data))
+/* compress context */
+struct compress_ctx {
+ unsigned int cluster_size; /* page count in cluster */
+ unsigned int log_cluster_size; /* log of cluster size */
+ void *rbuf; /* compression input buffer */
+ struct compress_data *cbuf; /* comprsssion output header + data */
+ size_t rlen; /* valid data length in rbuf */
+ size_t clen; /* valid data length in cbuf */
+ void *private; /* work buf for compress algorithm */
+};
+
#if HAVE_BYTESWAP_H
#include <byteswap.h>
#else
@@ -194,8 +227,8 @@ static inline uint64_t bswap_64(uint64_t val)
#define ASSERT(exp) \
do { \
if (!(exp)) { \
- printf("[ASSERT] (%s:%4d) " #exp"\n", \
- __func__, __LINE__); \
+ printf("[ASSERT] (%s:%4d) %s\n", \
+ __func__, __LINE__, #exp); \
exit(-1); \
} \
} while (0)
@@ -207,14 +240,14 @@ static inline uint64_t bswap_64(uint64_t val)
#define MSG(n, fmt, ...) \
do { \
- if (c.dbg_lv >= n) { \
+ if (c.dbg_lv >= n && !c.layout) { \
printf(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define DBG(n, fmt, ...) \
do { \
- if (c.dbg_lv >= n) { \
+ if (c.dbg_lv >= n && !c.layout) { \
printf("[%s:%4d] " fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} \
@@ -229,7 +262,11 @@ static inline uint64_t bswap_64(uint64_t val)
#define DISP_u16(ptr, member) \
do { \
assert(sizeof((ptr)->member) == 2); \
- printf("%-30s" "\t\t[0x%8x : %u]\n", \
+ if (c.layout) \
+ printf("%-30s %u\n", \
+ #member":", le16_to_cpu(((ptr)->member))); \
+ else \
+ printf("%-30s" "\t\t[0x%8x : %u]\n", \
#member, le16_to_cpu(((ptr)->member)), \
le16_to_cpu(((ptr)->member))); \
} while (0)
@@ -237,7 +274,11 @@ static inline uint64_t bswap_64(uint64_t val)
#define DISP_u32(ptr, member) \
do { \
assert(sizeof((ptr)->member) <= 4); \
- printf("%-30s" "\t\t[0x%8x : %u]\n", \
+ if (c.layout) \
+ printf("%-30s %u\n", \
+ #member":", le32_to_cpu(((ptr)->member))); \
+ else \
+ printf("%-30s" "\t\t[0x%8x : %u]\n", \
#member, le32_to_cpu(((ptr)->member)), \
le32_to_cpu(((ptr)->member))); \
} while (0)
@@ -245,14 +286,23 @@ static inline uint64_t bswap_64(uint64_t val)
#define DISP_u64(ptr, member) \
do { \
assert(sizeof((ptr)->member) == 8); \
- printf("%-30s" "\t\t[0x%8llx : %llu]\n", \
+ if (c.layout) \
+ printf("%-30s %llu\n", \
+ #member":", le64_to_cpu(((ptr)->member))); \
+ else \
+ printf("%-30s" "\t\t[0x%8llx : %llu]\n", \
#member, le64_to_cpu(((ptr)->member)), \
le64_to_cpu(((ptr)->member))); \
} while (0)
#define DISP_utf(ptr, member) \
do { \
- printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \
+ if (c.layout) \
+ printf("%-30s %s\n", #member":", \
+ ((ptr)->member)); \
+ else \
+ printf("%-30s" "\t\t[%s]\n", #member, \
+ ((ptr)->member)); \
} while (0)
/* Display to buffer */
@@ -311,6 +361,7 @@ enum f2fs_config_func {
DEFRAG,
RESIZE,
SLOAD,
+ LABEL,
};
enum default_set {
@@ -332,6 +383,7 @@ struct device_info {
u_int32_t nr_zones;
u_int32_t nr_rnd_zones;
size_t zone_blocks;
+ size_t *zone_cap_blocks;
};
typedef struct {
@@ -344,6 +396,47 @@ typedef struct {
bool dbg_en;
} dev_cache_config_t;
+/* f2fs_configration for compression used for sload.f2fs */
+typedef struct {
+ void (*init)(struct compress_ctx *cc);
+ int (*compress)(struct compress_ctx *cc);
+ void (*reset)(struct compress_ctx *cc);
+} compress_ops;
+
+/* Should be aligned to supported_comp_names and support_comp_ops */
+enum compress_algorithms {
+ COMPR_LZO,
+ COMPR_LZ4,
+ MAX_COMPRESS_ALGS,
+};
+
+enum filter_policy {
+ COMPR_FILTER_UNASSIGNED = 0,
+ COMPR_FILTER_ALLOW,
+ COMPR_FILTER_DENY,
+};
+
+typedef struct {
+ void (*add)(const char *);
+ void (*destroy)(void);
+ bool (*filter)(const char *);
+} filter_ops;
+
+typedef struct {
+ bool enabled; /* disabled by default */
+ bool required; /* require to enable */
+ bool readonly; /* readonly to release blocks */
+ struct compress_ctx cc; /* work context */
+ enum compress_algorithms alg; /* algorithm to compress */
+ compress_ops *ops; /* ops per algorithm */
+ unsigned int min_blocks; /* save more blocks than this */
+ enum filter_policy filter; /* filter to try compression */
+ filter_ops *filter_ops; /* filter ops */
+} compress_config_t;
+
+#define ALIGN_UP(value, size) ((value) + ((value) % (size) > 0 ? \
+ (size) - (value) % (size) : 0))
+
struct f2fs_configuration {
u_int32_t reserved_segments;
u_int32_t new_reserved_segments;
@@ -365,12 +458,14 @@ struct f2fs_configuration {
u_int64_t wanted_total_sectors;
u_int64_t wanted_sector_size;
u_int64_t target_sectors;
+ u_int64_t max_size;
u_int32_t sectors_per_blk;
u_int32_t blks_per_seg;
__u8 init_version[VERSION_LEN + 1];
__u8 sb_version[VERSION_LEN + 1];
__u8 version[VERSION_LEN + 1];
char *vol_label;
+ char *vol_uuid;
u_int16_t s_encoding;
u_int16_t s_encoding_flags;
int heap;
@@ -395,6 +490,7 @@ struct f2fs_configuration {
int bug_nat_bits;
int alloc_failed;
int auto_fix;
+ int layout;
int quota_fix;
int preen_mode;
int ro;
@@ -402,8 +498,10 @@ struct f2fs_configuration {
int large_nat_bitmap;
int fix_chksum; /* fix old cp.chksum position */
__le32 feature; /* defined features */
+ time_t fixed_time;
/* mkfs parameters */
+ int fake_seed;
u_int32_t next_free_nid;
u_int32_t quota_inum;
u_int32_t quota_dnum;
@@ -424,11 +522,11 @@ struct f2fs_configuration {
char *mount_point;
char *target_out_dir;
char *fs_config_file;
- time_t fixed_time;
#ifdef HAVE_LIBSELINUX
struct selinux_opt seopt_file[8];
int nr_opt;
#endif
+ int preserve_perms;
/* resize parameters */
int safe_resize;
@@ -438,6 +536,9 @@ struct f2fs_configuration {
/* cache parameters */
dev_cache_config_t cache_config;
+
+ /* compression support for sload.f2fs */
+ compress_config_t compress;
};
#ifdef CONFIG_64BIT
@@ -536,6 +637,7 @@ struct f2fs_configuration {
(void) (&_max1 == &_max2); \
_max1 > _max2 ? _max1 : _max2; })
+#define round_up(x, y) (((x) + (y) - 1) / (y))
/*
* Copied from fs/f2fs/f2fs.h
*/
@@ -615,7 +717,8 @@ enum {
#define F2FS_FEATURE_VERITY 0x0400 /* reserved */
#define F2FS_FEATURE_SB_CHKSUM 0x0800
#define F2FS_FEATURE_CASEFOLD 0x1000
- #define F2FS_FEATURE_COMPRESSION 0x2000
+#define F2FS_FEATURE_COMPRESSION 0x2000
+#define F2FS_FEATURE_RO 0x4000
#define MAX_VOLUME_NAME 512
@@ -786,6 +889,8 @@ struct f2fs_extent {
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */
+#define F2FS_PIN_FILE 0x40 /* file should not be gced */
+#define F2FS_COMPRESS_RELEASED 0x80 /* file released compressed blocks */
#if !defined(offsetof)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -1278,6 +1383,30 @@ static inline int get_inline_xattr_addrs(struct f2fs_inode *inode)
#ifdef HAVE_LINUX_BLKZONED_H
+/* Let's just use v2, since v1 should be compatible with v2 */
+#define BLK_ZONE_REP_CAPACITY (1 << 0)
+struct blk_zone_v2 {
+ __u64 start; /* Zone start sector */
+ __u64 len; /* Zone length in number of sectors */
+ __u64 wp; /* Zone write pointer position */
+ __u8 type; /* Zone type */
+ __u8 cond; /* Zone condition */
+ __u8 non_seq; /* Non-sequential write resources active */
+ __u8 reset; /* Reset write pointer recommended */
+ __u8 resv[4];
+ __u64 capacity; /* Zone capacity in number of sectors */
+ __u8 reserved[24];
+};
+#define blk_zone blk_zone_v2
+
+struct blk_zone_report_v2 {
+ __u64 sector;
+ __u32 nr_zones;
+ __u32 flags;
+ struct blk_zone zones[0];
+};
+#define blk_zone_report blk_zone_report_v2
+
#define blk_zone_type(z) (z)->type
#define blk_zone_conv(z) ((z)->type == BLK_ZONE_TYPE_CONVENTIONAL)
#define blk_zone_seq_req(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_REQ)
@@ -1324,13 +1453,17 @@ blk_zone_cond_str(struct blk_zone *blkz)
return "Unknown-cond";
}
+/*
+ * Handle kernel zone capacity support
+ */
#define blk_zone_empty(z) (blk_zone_cond(z) == BLK_ZONE_COND_EMPTY)
-
#define blk_zone_sector(z) (z)->start
#define blk_zone_length(z) (z)->len
#define blk_zone_wp_sector(z) (z)->wp
#define blk_zone_need_reset(z) (int)(z)->reset
#define blk_zone_non_seq(z) (int)(z)->non_seq
+#define blk_zone_capacity(z, f) ((f & BLK_ZONE_REP_CAPACITY) ? \
+ (z)->capacity : (z)->len)
#endif
@@ -1342,8 +1475,9 @@ extern int f2fs_report_zones(int, report_zones_cb_t *, void *);
extern int f2fs_check_zones(int);
int f2fs_reset_zone(int, void *);
extern int f2fs_reset_zones(int);
+extern uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb);
-#define SIZE_ALIGN(val, size) ((val) + (size) - 1) / (size)
+#define SIZE_ALIGN(val, size) (((val) + (size) - 1) / (size))
#define SEG_ALIGN(blks) SIZE_ALIGN(blks, c.blks_per_seg)
#define ZONE_ALIGN(blks) SIZE_ALIGN(blks, c.blks_per_seg * \
c.segs_per_zone)
@@ -1352,6 +1486,7 @@ static inline double get_best_overprovision(struct f2fs_super_block *sb)
{
double reserved, ovp, candidate, end, diff, space;
double max_ovp = 0, max_space = 0;
+ u_int32_t usable_main_segs = f2fs_get_usable_segments(sb);
if (get_sb(segment_count_main) < 256) {
candidate = 10;
@@ -1365,9 +1500,9 @@ static inline double get_best_overprovision(struct f2fs_super_block *sb)
for (; candidate <= end; candidate += diff) {
reserved = (2 * (100 / candidate + 1) + 6) *
- get_sb(segs_per_sec);
- ovp = (get_sb(segment_count_main) - reserved) * candidate / 100;
- space = get_sb(segment_count_main) - reserved - ovp;
+ round_up(usable_main_segs, get_sb(section_count));
+ ovp = (usable_main_segs - reserved) * candidate / 100;
+ space = usable_main_segs - reserved - ovp;
if (max_space < space) {
max_space = space;
max_ovp = candidate;
@@ -1435,6 +1570,7 @@ struct feature feature_table[] = { \
{ "sb_checksum", F2FS_FEATURE_SB_CHKSUM }, \
{ "casefold", F2FS_FEATURE_CASEFOLD }, \
{ "compression", F2FS_FEATURE_COMPRESSION }, \
+ { "ro", F2FS_FEATURE_RO}, \
{ NULL, 0x0}, \
};
@@ -1526,6 +1662,7 @@ extern const struct f2fs_nls_table *f2fs_load_nls_table(int encoding);
#define F2FS_ENC_UTF8_12_0 1
extern int f2fs_str2encoding(const char *string);
+extern char *f2fs_encoding2str(const int encoding);
extern int f2fs_get_encoding_flags(int encoding);
extern int f2fs_str2encoding_flags(char **param, __u16 *flags);
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 55d3a5c..0add901 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -637,20 +637,25 @@ char *get_rootdev()
ptr = strstr(uevent, "DEVNAME");
if (!ptr)
- return NULL;
+ goto out_free;
ret = sscanf(ptr, "DEVNAME=%s\n", buf);
if (strlen(buf) == 0)
- return NULL;
+ goto out_free;
ret = strlen(buf) + 5;
rootdev = malloc(ret + 1);
if (!rootdev)
- return NULL;
+ goto out_free;
rootdev[ret] = '\0';
snprintf(rootdev, ret + 1, "/dev/%s", buf);
+ free(uevent);
return rootdev;
+
+out_free:
+ free(uevent);
+ return NULL;
#endif
}
@@ -830,7 +835,7 @@ void get_kernel_uname_version(__u8 *version)
if (uname(&buf))
return;
-#if !defined(WITH_KERNEL_VERSION)
+#if defined(WITH_KERNEL_VERSION)
snprintf((char *)version,
VERSION_LEN, "%s %s", buf.release, buf.version);
#else
@@ -1158,6 +1163,8 @@ int get_device_info(int i)
c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size;
c.total_sectors += dev->total_sectors;
+ if (c.sparse_mode && f2fs_init_sparse_file())
+ return -1;
return 0;
}
#endif
@@ -1304,6 +1311,17 @@ int f2fs_str2encoding(const char *string)
return -EINVAL;
}
+char *f2fs_encoding2str(const int encoding)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(f2fs_encoding_map); i++)
+ if (f2fs_encoding_map[i].encoding_magic == encoding)
+ return f2fs_encoding_map[i].name;
+
+ return NULL;
+}
+
int f2fs_get_encoding_flags(int encoding)
{
int i;
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index 1f597a9..320ee6c 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -5,6 +5,9 @@
* http://www.samsung.com/
* Copyright (c) 2019 Google Inc.
* http://www.google.com/
+ * Copyright (c) 2020 Google Inc.
+ * Robin Hsu <robinhsu@google.com>
+ * : add quick-buffer for sload compression support
*
* Dual licensed under the GPL or LGPL version 2 licenses.
*/
@@ -504,6 +507,9 @@ int dev_read(void *buf, __u64 offset, size_t len)
int fd;
int err;
+ if (c.max_size < (offset + len))
+ c.max_size = offset + len;
+
if (c.sparse_mode)
return sparse_read_blk(offset / F2FS_BLKSIZE,
len / F2FS_BLKSIZE, buf);
@@ -545,6 +551,9 @@ int dev_write(void *buf, __u64 offset, size_t len)
{
int fd;
+ if (c.max_size < (offset + len))
+ c.max_size = offset + len;
+
if (c.dry_run)
return 0;
@@ -587,6 +596,9 @@ int dev_fill(void *buf, __u64 offset, size_t len)
{
int fd;
+ if (c.max_size < (offset + len))
+ c.max_size = offset + len;
+
if (c.sparse_mode)
return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE,
len / F2FS_BLKSIZE);
@@ -784,6 +796,7 @@ int f2fs_finalize_device(void)
break;
}
free(c.devices[i].path);
+ free(c.devices[i].zone_cap_blocks);
}
close(c.kd);
diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c
index efc687c..ce73b9a 100644
--- a/lib/libf2fs_zoned.c
+++ b/lib/libf2fs_zoned.c
@@ -291,6 +291,13 @@ int f2fs_check_zones(int j)
return -ENOMEM;
}
+ dev->zone_cap_blocks = malloc(dev->nr_zones * sizeof(size_t));
+ if (!dev->zone_cap_blocks) {
+ ERR_MSG("No memory for zone capacity list.\n");
+ return -ENOMEM;
+ }
+ memset(dev->zone_cap_blocks, 0, (dev->nr_zones * sizeof(size_t)));
+
dev->nr_rnd_zones = 0;
sector = 0;
total_sectors = (dev->total_sectors * c.sector_size) >> 9;
@@ -335,10 +342,15 @@ int f2fs_check_zones(int j)
blk_zone_cond_str(blkz),
blk_zone_sector(blkz),
blk_zone_length(blkz));
+ dev->zone_cap_blocks[n] =
+ blk_zone_length(blkz) >>
+ (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
} else {
DBG(2,
- "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, "
- "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n",
+ "Zone %05u: type 0x%x (%s), cond 0x%x (%s),"
+ " need_reset %d, non_seq %d, sector %llu,"
+ " %llu sectors, capacity %llu,"
+ " wp sector %llu\n",
n,
blk_zone_type(blkz),
blk_zone_type_str(blkz),
@@ -348,7 +360,11 @@ int f2fs_check_zones(int j)
blk_zone_non_seq(blkz),
blk_zone_sector(blkz),
blk_zone_length(blkz),
+ blk_zone_capacity(blkz, rep->flags),
blk_zone_wp_sector(blkz));
+ dev->zone_cap_blocks[n] =
+ blk_zone_capacity(blkz, rep->flags) >>
+ (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
}
sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
@@ -473,6 +489,37 @@ out:
return ret;
}
+uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
+{
+#ifdef HAVE_BLK_ZONE_REP_V2
+ int i, j;
+ uint32_t usable_segs = 0, zone_segs;
+
+ if (c.func == RESIZE)
+ return get_sb(segment_count_main);
+
+ for (i = 0; i < c.ndevs; i++) {
+ if (c.devices[i].zoned_model != F2FS_ZONED_HM) {
+ usable_segs += c.devices[i].total_segments;
+ continue;
+ }
+ for (j = 0; j < c.devices[i].nr_zones; j++) {
+ zone_segs = c.devices[i].zone_cap_blocks[j] >>
+ get_sb(log_blocks_per_seg);
+ if (c.devices[i].zone_cap_blocks[j] %
+ DEFAULT_BLOCKS_PER_SEGMENT)
+ usable_segs += zone_segs + 1;
+ else
+ usable_segs += zone_segs;
+ }
+ }
+ usable_segs -= (get_sb(main_blkaddr) - get_sb(segment0_blkaddr)) >>
+ get_sb(log_blocks_per_seg);
+ return usable_segs;
+#endif
+ return get_sb(segment_count_main);
+}
+
#else
int f2fs_report_zone(int i, u_int64_t UNUSED(sector), void *UNUSED(blkzone))
@@ -527,5 +574,9 @@ int f2fs_reset_zones(int i)
return -1;
}
+uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
+{
+ return get_sb(segment_count_main);
+}
#endif
diff --git a/man/Makefile.am b/man/Makefile.am
index 1d16c6f..9363b82 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
## Makefile.am
-dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8 f2fs_io.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8 f2fs_io.8 f2fslabel.8
diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
index b08399b..34113de 100644
--- a/man/defrag.f2fs.8
+++ b/man/defrag.f2fs.8
@@ -48,7 +48,7 @@ is 0 on success and -1 on failure.
Specify the starting block address.
.TP
.BI \-l " number of blocks"
-Specifiy the number of blocks to move.
+Specify the number of blocks to move.
.TP
.BI \-t " target block address"
Specify the destination block address.
diff --git a/man/f2fslabel.8 b/man/f2fslabel.8
new file mode 100644
index 0000000..848ed3b
--- /dev/null
+++ b/man/f2fslabel.8
@@ -0,0 +1,33 @@
+.\" Copyright (c) 2021 Samsung Electronics Co., Ltd.
+.\"
+.TH F2FSLABEL 8
+.SH NAME
+f2fslabel \- Change the label on an f2fs volume
+.SH SYNOPSIS
+.B f2fslabel
+.I device
+[
+.I volume-label
+]
+.SH DESCRIPTION
+.B f2fslabel
+will display or change the volume label on the f2fs located on
+.I device.
+.PP
+If the optional argument
+.I volume-label
+is present, then
+.B f2fslabel
+will set the volume label to be
+.IR volume-label .
+.PP
+Otherwise,
+.B f2fslabel
+will simply show the current label.
+.PP
+.SH AUTHOR
+.B f2fslabel
+was written by Dongwoo Lee (dwoo08.lee@samsung.com).
+.SH AVAILABILITY
+.B f2fslabel
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index 022941f..15e0bd9 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -32,6 +32,7 @@ mkfs.f2fs \- create an F2FS file system
]
[
.B \-g
+.I default-options
]
[
.B \-i
@@ -59,6 +60,9 @@ mkfs.f2fs \- create an F2FS file system
.B \-q
]
[
+.B \-r
+]
+[
.B \-R
.I root_owner
]
@@ -74,6 +78,10 @@ mkfs.f2fs \- create an F2FS file system
.I nodiscard/discard
]
[
+.B \-T
+.I timestamp
+]
+[
.B \-w
.I wanted-sector-size
]
@@ -91,7 +99,7 @@ mkfs.f2fs \- create an F2FS file system
is used to create a f2fs file system (usually in a disk partition).
\fIdevice\fP is the special file corresponding to the device (e.g.
\fI/dev/sdXX\fP).
-\fIsectors\fP is optionally given for specifing the filesystem size.
+\fIsectors\fP is optionally given for specifying the filesystem size.
.PP
The exit code returned by
.B mkfs.f2fs
@@ -129,11 +137,18 @@ 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 \-g
-Add default Android options.
+.BI \-g " default-options"
+Use a default set of options.
+The following values are supported:
+.RS 1.2i
+.TP 1.2i
+.B android
+Use default options for Android.
+.RE
.TP
.BI \-i
Enable extended node bitmap.
+.TP
.BI \-l " volume-label"
Specify the volume label to the partition mounted as F2FS.
.TP
@@ -176,7 +191,7 @@ Enable inode creation time feature. Requires extra attr.
Enable lost+found feature.
.TP
.B verity
-Reserved feature.
+Enable support for verity protected files (a.k.a. fs-verity).
.TP
.B sb_checksum
Enable superblock checksum.
@@ -212,6 +227,9 @@ Default is disabled.
Quiet mode.
With it, mkfs.f2fs does not show any messages, including the basic messages.
.TP
+.BI \-r
+Sets the checkpointing srand seed to 0.
+.TP
.BI \-R
Give root_owner option for initial uid/gid assignment.
Default is set by getuid()/getgid(), and assigned by "-R $uid:$gid".
@@ -228,6 +246,10 @@ Enable sparse mode.
Specify 1 or 0 to enable or disable discard policy, respectively.
The default value is 1.
.TP
+.BI \-T " timestamp"
+Set inodes times to a given timestamp. By default, the current time will be used.
+This behaviour corresponds to the value -1.
+.TP
.BI \-w " wanted-sector-size"
Specify the sector size in bytes.
Without it, the sectors will be calculated by device sector size.
@@ -242,6 +264,9 @@ Number of sectors. Default is determined by device size.
.TP
.BI \-V
Print the version number and exit.
+.TP
+.BI \-h,\ \-\-help
+Print usage and exit.
.SH AUTHOR
This version of
.B mkfs.f2fs
diff --git a/man/sload.f2fs.8 b/man/sload.f2fs.8
index d07330c..ed5ee4b 100644
--- a/man/sload.f2fs.8
+++ b/man/sload.f2fs.8
@@ -7,22 +7,51 @@ sload.f2fs \- load directories and files into the device directly
.B sload.f2fs
[
.B \-f
-.I source directory path
+.I source-directory-path
]
[
.B \-t
-.I mount point
+.I mount-point
]
[
.B \-d
.I debugging-level
]
+[
+.B \-P
+]
+[
+.B \-c
+[
+.B \-L
+.I log-of-blocks-per-cluster
+]
+[
+.B \-a
+.I compression-algorithm
+]
+[
+.B \-x
+.I file-extension-to-exclude-from-compression
+|
+.B \-i
+.I file-extension-to-include-for-compression
+]
+[
+.B \-m
+.I minimum-compressed-blocks-per-cluster
+]
+[
+.B \-r
+]
+]
.I device
.SH DESCRIPTION
.B sload.f2fs
-is used to load directories and files into a disk partition.
-\fIdevice\fP is the special file corresponding to the device (e.g.
-\fI/dev/sdXX\fP).
+is used to load directories and files into a disk partition, or an F2FS
+image (file).
+\fIdevice\fP could a special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP), or an F2FS image file.
.PP
The exit code returned by
@@ -30,24 +59,76 @@ The exit code returned by
is 0 on success and -1 on failure.
.SH OPTIONS
.TP
-.BI \-f " source directory path"
+.BI \-f " source-directory-path"
Specify the source directory path to be loaded.
.TP
-.BI \-t " mount point path"
+.BI \-t " mount-point-path"
Specify the mount point path in the partition to load.
.TP
.BI \-d " debug-level"
Specify the level of debugging options.
The default number is 0, which shows basic debugging messages.
.TP
+.BI \-P
+Preserve owner: user and group.
+The user and group of the source files will be taken into account.
+.TP
+.BI \-c
+Enable a cluster-based file compression.
+The file would be chopped into clusters, and each cluster is compressed
+independently.
+.TP
+.BI \-L " log-of-blocks-per-cluster
+Specify cluster size in power of two blocks.
+The minimum value is 2 (4 blocks, default).
+The maximum value is 8 (256 blocks).
+Note that a block contains 4096 bytes.
+This option must be used with option \fB\-c\fR.
+.TP
+.BI \-a " compression-algorithm"
+Choose the algorithm for compression. Available options are:
+lzo, lz4 (default).
+This option must be used with option \fB\-c\fR.
+.TP
+.BI \-i " file-extension-to-include-for-compression"
+Specify a file extension to include for the compression.
+To specify multiple file extensions, use multiple option \fB\-i\fR's.
+Files having one of the listed extensions will be compressed.
+This option must be used with option \fB\-c\fR.
+.TP
+.BI \-x " file-extension-to-exclude-from-compression"
+Specify a file extension to exclude from compression.
+To specify multiple file extensions, use multiple option \fB\-x\fR's.
+Files having one of the listed extensions won't be compressed.
+This option must be used with option \fB\-c\fR.
+.TP
+.BI \-m " minimum-compressed-blocks-per-cluster"
+Specify a minimum block count saved (by compression) per cluster.
+The minimum value is 1 (default).
+Maximum value is the cluster size in blocks minus 1.
+If compression of a cluster fails to save at least the minimum compressed
+block count given by the option, the cluster will not be compressed.
+This option must be used with option \fB\-c\fR.
+.TP
+.BI \-r
+Specify read-only flag for the compressed files.
+It allows filesystem to release compressed space to the users, since, without
+this option, filesystem should keep the space for future file updates.
+This option must be used with option \fB\-c\fR.
+
+.SH NOTES
+If neither \fB\-i\fR nor \fB\-x\fR is used, all files will be compressed.
+Obviously, option \fB\-i\fR and \fB-x\fR can not be used together.
+
.SH AUTHOR
This version of
.B sload.f2fs
-has been written by Hou Pengyang <houpengyang@huawei.com>,
-Liu Shuoran <liushuoran@huawei.com>, Jaegeuk Kim <jaegeuk@kernel.org>
+has been contributed by Hou Pengyang <houpengyang@huawei.com>,
+Liu Shuoran <liushuoran@huawei.com>, Jaegeuk Kim <jaegeuk@kernel.org>,
+Robin Hsu <robinhsu@google.com>
.SH AVAILABILITY
.B sload.f2fs
-is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+is available from <git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git>.
.SH SEE ALSO
.BR mkfs.f2fs(8),
.BR fsck.f2fs(8),
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index 83e2389..af5b1c7 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -11,8 +11,7 @@ mkfs_f2fs_LDADD = ${libuuid_LIBS} ${libblkid_LIBS} $(top_builddir)/lib/libf2fs.l
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 = ${libblkid_LIBS} -luuid -L$(top_builddir)/lib -lf2fs \
+libf2fs_format_la_LDFLAGS = ${libblkid_LIBS} ${libuuid_LIBS} -L$(top_builddir)/lib -lf2fs \
-version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE)
install-exec-hook:
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 44575e0..3565bd3 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -18,7 +18,7 @@
#include <sys/mount.h>
#endif
#include <time.h>
-#include <uuid/uuid.h>
+#include <uuid.h>
#include "f2fs_fs.h"
#include "quota.h"
@@ -35,6 +35,9 @@ struct f2fs_checkpoint *cp;
#define last_zone(cur) ((cur - 1) * c.segs_per_zone)
#define last_section(cur) (cur + (c.secs_per_zone - 1) * c.segs_per_sec)
+/* Return time fixed by the user or current time by default */
+#define mkfs_time ((c.fixed_time == -1) ? time(NULL) : c.fixed_time)
+
static unsigned int quotatype_bits = 0;
const char *media_ext_lists[] = {
@@ -93,6 +96,13 @@ const char *media_ext_lists[] = {
const char *hot_ext_lists[] = {
"db",
+
+#ifndef WITH_ANDROID
+ /* Virtual machines */
+ "vmdk", // VMware or VirtualBox
+ "vdi", // VirtualBox
+ "qcow2", // QEMU
+#endif
NULL
};
@@ -202,7 +212,7 @@ static int f2fs_prepare_super_block(void)
u_int64_t total_meta_zones, total_meta_segments;
u_int32_t sit_bitmap_size, max_sit_bitmap_size;
u_int32_t max_nat_bitmap_size, max_nat_segments;
- u_int32_t total_zones;
+ u_int32_t total_zones, avail_zones;
enum quota_type qtype;
int i;
@@ -240,6 +250,9 @@ static int f2fs_prepare_super_block(void)
zone_size_bytes * zone_size_bytes -
(u_int64_t) c.start_sector * DEFAULT_SECTOR_SIZE;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+ zone_align_start_offset = 8192;
+
if (c.start_sector % DEFAULT_SECTORS_PER_BLOCK) {
MSG(1, "\t%s: Align start sector number to the page unit\n",
c.zoned_mode ? "FAIL" : "WARN");
@@ -251,14 +264,22 @@ static int f2fs_prepare_super_block(void)
return -1;
}
+ if (c.zoned_mode && c.ndevs > 1)
+ zone_align_start_offset +=
+ (c.devices[0].total_sectors * c.sector_size) % zone_size_bytes;
+
set_sb(segment0_blkaddr, zone_align_start_offset / blk_size_bytes);
sb->cp_blkaddr = sb->segment0_blkaddr;
MSG(0, "Info: zone aligned segment0 blkaddr: %u\n",
get_sb(segment0_blkaddr));
- if (c.zoned_mode && (get_sb(segment0_blkaddr) + c.start_sector /
- DEFAULT_SECTORS_PER_BLOCK) % c.zone_blocks) {
+ if (c.zoned_mode &&
+ ((c.ndevs == 1 &&
+ (get_sb(segment0_blkaddr) + c.start_sector /
+ DEFAULT_SECTORS_PER_BLOCK) % c.zone_blocks) ||
+ (c.ndevs > 1 &&
+ c.devices[1].start_blkaddr % c.zone_blocks))) {
MSG(1, "\tError: Unaligned segment0 block address %u\n",
get_sb(segment0_blkaddr));
return -1;
@@ -382,7 +403,10 @@ static int f2fs_prepare_super_block(void)
get_sb(segment_count_nat))) *
c.blks_per_seg;
- blocks_for_ssa = total_valid_blks_available /
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+ blocks_for_ssa = 0;
+ else
+ blocks_for_ssa = total_valid_blks_available /
c.blks_per_seg + 1;
set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
@@ -425,15 +449,27 @@ static int f2fs_prepare_super_block(void)
set_sb(segment_count_main, get_sb(section_count) * c.segs_per_sec);
- /* Let's determine the best reserved and overprovisioned space */
+ /*
+ * Let's determine the best reserved and overprovisioned space.
+ * For Zoned device, if zone capacity less than zone size, the segments
+ * starting after the zone capacity are unusable in each zone. So get
+ * overprovision ratio and reserved seg count based on avg usable
+ * segs_per_sec.
+ */
if (c.overprovision == 0)
c.overprovision = get_best_overprovision(sb);
c.reserved_segments =
- (2 * (100 / c.overprovision + 1) + NR_CURSEG_TYPE)
- * c.segs_per_sec;
+ (2 * (100 / c.overprovision + 1) + NR_CURSEG_TYPE) *
+ round_up(f2fs_get_usable_segments(sb), get_sb(section_count));
- if (c.overprovision == 0 || c.total_segments < F2FS_MIN_SEGMENTS ||
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+ c.overprovision = 0;
+ c.reserved_segments = 0;
+ }
+ if ((!(c.feature & cpu_to_le32(F2FS_FEATURE_RO)) &&
+ c.overprovision == 0) ||
+ c.total_segments < F2FS_MIN_SEGMENTS ||
(c.devices[0].total_sectors *
c.sector_size < zone_align_start_offset) ||
(get_sb(segment_count_main) - NR_CURSEG_TYPE) <
@@ -442,7 +478,14 @@ static int f2fs_prepare_super_block(void)
return -1;
}
- uuid_generate(sb->uuid);
+ if (c.vol_uuid) {
+ if (uuid_parse(c.vol_uuid, sb->uuid)) {
+ MSG(0, "\tError: supplied string is not a valid UUID\n");
+ return -1;
+ }
+ } else {
+ uuid_generate(sb->uuid);
+ }
/* precompute checksum seed for metadata */
if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
@@ -472,13 +515,25 @@ static int f2fs_prepare_super_block(void)
if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND))
c.lpf_ino = c.next_free_nid++;
- if (total_zones <= 6) {
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+ avail_zones = 2;
+ else
+ avail_zones = 6;
+
+ if (total_zones <= avail_zones) {
MSG(1, "\tError: %d zones: Need more zones "
"by shrinking zone size\n", total_zones);
return -1;
}
- if (c.heap) {
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+ c.cur_seg[CURSEG_HOT_NODE] = 0;
+ c.cur_seg[CURSEG_WARM_NODE] = 0;
+ c.cur_seg[CURSEG_COLD_NODE] = 0;
+ c.cur_seg[CURSEG_HOT_DATA] = 1;
+ c.cur_seg[CURSEG_COLD_DATA] = 0;
+ c.cur_seg[CURSEG_WARM_DATA] = 0;
+ } else if (c.heap) {
c.cur_seg[CURSEG_HOT_NODE] =
last_section(last_zone(total_zones));
c.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
@@ -486,6 +541,13 @@ static int f2fs_prepare_super_block(void)
c.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE);
c.cur_seg[CURSEG_COLD_DATA] = 0;
c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA);
+ } else if (c.zoned_mode) {
+ c.cur_seg[CURSEG_HOT_NODE] = 0;
+ c.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
+ c.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE);
+ c.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE);
+ c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_HOT_DATA);
+ c.cur_seg[CURSEG_COLD_DATA] = next_zone(CURSEG_WARM_DATA);
} else {
c.cur_seg[CURSEG_HOT_NODE] = 0;
c.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE);
@@ -500,7 +562,8 @@ static int f2fs_prepare_super_block(void)
}
/* if there is redundancy, reassign it */
- verify_cur_segs();
+ if (!(c.feature & cpu_to_le32(F2FS_FEATURE_RO)))
+ verify_cur_segs();
cure_extension_list();
@@ -654,7 +717,7 @@ static int f2fs_write_check_point_pack(void)
}
/* 1. cp page 1 of checkpoint pack 1 */
- srand(time(NULL));
+ srand((c.fake_seed) ? 0 : time(NULL));
cp->checkpoint_ver = cpu_to_le64(rand() | 0x1);
set_cp(cur_node_segno[0], c.cur_seg[CURSEG_HOT_NODE]);
set_cp(cur_node_segno[1], c.cur_seg[CURSEG_WARM_NODE]);
@@ -672,21 +735,36 @@ static int f2fs_write_check_point_pack(void)
set_cp(valid_block_count, 2 + c.quota_inum + c.quota_dnum +
c.lpf_inum + c.lpf_dnum);
set_cp(rsvd_segment_count, c.reserved_segments);
- set_cp(overprov_segment_count, (get_sb(segment_count_main) -
+
+ /*
+ * For zoned devices, if zone capacity less than zone size, get
+ * overprovision segment count based on usable segments in the device.
+ */
+ set_cp(overprov_segment_count, (f2fs_get_usable_segments(sb) -
get_cp(rsvd_segment_count)) *
c.overprovision / 100);
set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
get_cp(rsvd_segment_count));
+ if (f2fs_get_usable_segments(sb) <= get_cp(overprov_segment_count)) {
+ MSG(0, "\tError: Not enough segments to create F2FS Volume\n");
+ goto free_cp_payload;
+ }
MSG(0, "Info: Overprovision ratio = %.3lf%%\n", c.overprovision);
MSG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n",
get_cp(overprov_segment_count),
c.reserved_segments);
/* main segments - reserved segments - (node + data segments) */
- set_cp(free_segment_count, get_sb(segment_count_main) - 6);
- set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+ set_cp(free_segment_count, f2fs_get_usable_segments(sb) - 2);
+ set_cp(user_block_count, ((get_cp(free_segment_count) + 2 -
get_cp(overprov_segment_count)) * c.blks_per_seg));
+ } else {
+ set_cp(free_segment_count, f2fs_get_usable_segments(sb) - 6);
+ set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
+ 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));
flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG;
@@ -800,8 +878,13 @@ static int f2fs_write_check_point_pack(void)
sum_compact_p += SUM_JOURNAL_SIZE;
memset(sum, 0, sizeof(struct f2fs_summary_block));
+
/* inode sit for root */
- journal->n_sits = cpu_to_le16(6);
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+ journal->n_sits = cpu_to_le16(2);
+ else
+ journal->n_sits = cpu_to_le16(6);
+
journal->sit_j.entries[0].segno = cp->cur_node_segno[0];
journal->sit_j.entries[0].se.vblocks =
cpu_to_le16((CURSEG_HOT_NODE << 10) |
@@ -812,30 +895,43 @@ static int f2fs_write_check_point_pack(void)
if (c.lpf_inum)
f2fs_set_bit(i, (char *)journal->sit_j.entries[0].se.valid_map);
- journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
- journal->sit_j.entries[1].se.vblocks =
- cpu_to_le16((CURSEG_WARM_NODE << 10));
- journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
- journal->sit_j.entries[2].se.vblocks =
- cpu_to_le16((CURSEG_COLD_NODE << 10));
-
- /* data sit for root */
- journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
- journal->sit_j.entries[3].se.vblocks =
- cpu_to_le16((CURSEG_HOT_DATA << 10) |
- (1 + c.quota_dnum + c.lpf_dnum));
- f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
- for (i = 1; i <= c.quota_dnum; i++)
- f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
- if (c.lpf_dnum)
- f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
-
- journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
- journal->sit_j.entries[4].se.vblocks =
- cpu_to_le16((CURSEG_WARM_DATA << 10));
- journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
- journal->sit_j.entries[5].se.vblocks =
- cpu_to_le16((CURSEG_COLD_DATA << 10));
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+ /* data sit for root */
+ journal->sit_j.entries[1].segno = cp->cur_data_segno[0];
+ journal->sit_j.entries[1].se.vblocks =
+ cpu_to_le16((CURSEG_HOT_DATA << 10) |
+ (1 + c.quota_dnum + c.lpf_dnum));
+ f2fs_set_bit(0, (char *)journal->sit_j.entries[1].se.valid_map);
+ for (i = 1; i <= c.quota_dnum; i++)
+ f2fs_set_bit(i, (char *)journal->sit_j.entries[1].se.valid_map);
+ if (c.lpf_dnum)
+ f2fs_set_bit(i, (char *)journal->sit_j.entries[1].se.valid_map);
+ } else {
+ journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
+ journal->sit_j.entries[1].se.vblocks =
+ cpu_to_le16((CURSEG_WARM_NODE << 10));
+ journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
+ journal->sit_j.entries[2].se.vblocks =
+ cpu_to_le16((CURSEG_COLD_NODE << 10));
+
+ /* data sit for root */
+ journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
+ journal->sit_j.entries[3].se.vblocks =
+ cpu_to_le16((CURSEG_HOT_DATA << 10) |
+ (1 + c.quota_dnum + c.lpf_dnum));
+ f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
+ for (i = 1; i <= c.quota_dnum; i++)
+ f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
+ if (c.lpf_dnum)
+ f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
+
+ journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
+ journal->sit_j.entries[4].se.vblocks =
+ cpu_to_le16((CURSEG_WARM_DATA << 10));
+ journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
+ journal->sit_j.entries[5].se.vblocks =
+ cpu_to_le16((CURSEG_COLD_DATA << 10));
+ }
memcpy(sum_compact_p, &journal->n_sits, SUM_JOURNAL_SIZE);
sum_compact_p += SUM_JOURNAL_SIZE;
@@ -1043,7 +1139,7 @@ static int f2fs_discard_obsolete_dnode(void)
u_int64_t start_inode_pos = get_sb(main_blkaddr);
u_int64_t last_inode_pos;
- if (c.zoned_mode)
+ if (c.zoned_mode || c.feature & cpu_to_le32(F2FS_FEATURE_RO))
return 0;
raw_node = calloc(sizeof(struct f2fs_node), 1);
@@ -1121,11 +1217,11 @@ static int f2fs_write_root_inode(void)
raw_node->i.i_size = cpu_to_le64(1 * blk_size_bytes); /* dentry */
raw_node->i.i_blocks = cpu_to_le64(2);
- raw_node->i.i_atime = cpu_to_le32(time(NULL));
+ raw_node->i.i_atime = cpu_to_le32(mkfs_time);
raw_node->i.i_atime_nsec = 0;
- raw_node->i.i_ctime = cpu_to_le32(time(NULL));
+ raw_node->i.i_ctime = cpu_to_le32(mkfs_time);
raw_node->i.i_ctime_nsec = 0;
- raw_node->i.i_mtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_mtime = cpu_to_le32(mkfs_time);
raw_node->i.i_mtime_nsec = 0;
raw_node->i.i_generation = 0;
raw_node->i.i_xattr_nid = 0;
@@ -1142,7 +1238,7 @@ static int f2fs_write_root_inode(void)
raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) {
- raw_node->i.i_crtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_crtime = cpu_to_le32(mkfs_time);
raw_node->i.i_crtime_nsec = 0;
}
@@ -1279,11 +1375,11 @@ static int f2fs_write_qf_inode(int qtype)
raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */
raw_node->i.i_blocks = cpu_to_le64(1 + QUOTA_DATA(qtype));
- raw_node->i.i_atime = cpu_to_le32(time(NULL));
+ raw_node->i.i_atime = cpu_to_le32(mkfs_time);
raw_node->i.i_atime_nsec = 0;
- raw_node->i.i_ctime = cpu_to_le32(time(NULL));
+ raw_node->i.i_ctime = cpu_to_le32(mkfs_time);
raw_node->i.i_ctime_nsec = 0;
- raw_node->i.i_mtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_mtime = cpu_to_le32(mkfs_time);
raw_node->i.i_mtime_nsec = 0;
raw_node->i.i_generation = 0;
raw_node->i.i_xattr_nid = 0;
@@ -1474,11 +1570,11 @@ static int f2fs_write_lpf_inode(void)
raw_node->i.i_size = cpu_to_le64(1 * blk_size_bytes);
raw_node->i.i_blocks = cpu_to_le64(2);
- raw_node->i.i_atime = cpu_to_le32(time(NULL));
+ raw_node->i.i_atime = cpu_to_le32(mkfs_time);
raw_node->i.i_atime_nsec = 0;
- raw_node->i.i_ctime = cpu_to_le32(time(NULL));
+ raw_node->i.i_ctime = cpu_to_le32(mkfs_time);
raw_node->i.i_ctime_nsec = 0;
- raw_node->i.i_mtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_mtime = cpu_to_le32(mkfs_time);
raw_node->i.i_mtime_nsec = 0;
raw_node->i.i_generation = 0;
raw_node->i.i_xattr_nid = 0;
@@ -1498,7 +1594,7 @@ static int f2fs_write_lpf_inode(void)
raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) {
- raw_node->i.i_crtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_crtime = cpu_to_le32(mkfs_time);
raw_node->i.i_crtime_nsec = 0;
}
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index 204a410..03eb748 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -18,12 +18,13 @@
#include <sys/mount.h>
#endif
#include <time.h>
-#include <uuid/uuid.h>
+#include <uuid.h>
#include <errno.h>
+#include <getopt.h>
#include "config.h"
#ifdef HAVE_LIBBLKID
-# include <blkid/blkid.h>
+# include <blkid.h>
#endif
#include "f2fs_fs.h"
@@ -52,15 +53,18 @@ static void mkfs_usage()
MSG(0, " -g add default options\n");
MSG(0, " -i extended node bitmap, node ratio is 20%% by default\n");
MSG(0, " -l label\n");
+ MSG(0, " -U uuid\n");
MSG(0, " -m support zoned block device [default:0]\n");
MSG(0, " -o overprovision percentage [default:auto]\n");
MSG(0, " -O feature1[,feature2,...] e.g. \"encrypt\"\n");
MSG(0, " -C [encoding[:flag1,...]] Support casefolding with optional flags\n");
MSG(0, " -q quiet mode\n");
+ MSG(0, " -r set checkpointing seed (srand()) to 0\n");
MSG(0, " -R root_owner [default: 0:0]\n");
MSG(0, " -s # of segments per section [default:1]\n");
MSG(0, " -S sparse mode\n");
MSG(0, " -t 0: nodiscard, 1: discard [default:1]\n");
+ MSG(0, " -T timestamps\n");
MSG(0, " -w wanted sector size\n");
MSG(0, " -z # of sections per zone [default:1]\n");
MSG(0, " -V print the version number and exit\n");
@@ -88,6 +92,15 @@ static void f2fs_show_info()
if (c.defset == CONF_ANDROID)
MSG(0, "Info: Set conf for android\n");
+
+ if (c.feature & le32_to_cpu(F2FS_FEATURE_CASEFOLD))
+ MSG(0, "Info: Enable %s with casefolding\n",
+ f2fs_encoding2str(c.s_encoding));
+ if (c.feature & le32_to_cpu(F2FS_FEATURE_PRJQUOTA))
+ MSG(0, "Info: Enable Project quota\n");
+
+ if (c.feature & le32_to_cpu(F2FS_FEATURE_COMPRESSION))
+ MSG(0, "Info: Enable Compression\n");
}
static void add_default_options(void)
@@ -104,16 +117,28 @@ static void add_default_options(void)
c.root_uid = c.root_gid = 0;
break;
}
+#ifdef CONF_CASEFOLD
+ c.s_encoding = F2FS_ENC_UTF8_12_1;
+ c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
+#endif
+#ifdef CONF_PROJID
+ c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA);
+ c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR);
+#endif
}
static void f2fs_parse_options(int argc, char *argv[])
{
- static const char *option_string = "qa:c:C:d:e:E:g:il:mo:O:R:s:S:z:t:Vfw:";
+ static const char *option_string = "qa:c:C:d:e:E:g:hil:mo:O:rR:s:S:z:t:T:U:Vfw:";
+ static const struct option long_opts[] = {
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = NULL, .has_arg = 0, .flag = NULL, .val = 0 }
+ };
int32_t option=0;
int val;
char *token;
- while ((option = getopt(argc,argv,option_string)) != EOF) {
+ while ((option = getopt_long(argc,argv,option_string,long_opts,NULL)) != EOF) {
switch (option) {
case 'q':
c.dbg_lv = -1;
@@ -147,6 +172,9 @@ static void f2fs_parse_options(int argc, char *argv[])
if (!strcmp(optarg, "android"))
c.defset = CONF_ANDROID;
break;
+ case 'h':
+ mkfs_usage();
+ break;
case 'i':
c.large_nat_bitmap = 1;
break;
@@ -168,6 +196,9 @@ static void f2fs_parse_options(int argc, char *argv[])
if (parse_feature(feature_table, optarg))
mkfs_usage();
break;
+ case 'r':
+ c.fake_seed = 1;
+ break;
case 'R':
if (parse_root_owner(optarg, &c.root_uid, &c.root_gid))
mkfs_usage();
@@ -186,6 +217,12 @@ static void f2fs_parse_options(int argc, char *argv[])
case 't':
c.trim = atoi(optarg);
break;
+ case 'T':
+ c.fixed_time = strtoul(optarg, NULL, 0);
+ break;
+ case 'U':
+ c.vol_uuid = strdup(optarg);
+ break;
case 'f':
force_overwrite = 1;
break;
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 446bb39..56bf2e4 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -10,7 +10,7 @@ parse_f2fs_SOURCES = f2fs_io_parse.c
if LINUX
sbin_PROGRAMS += f2fscrypt
f2fscrypt_SOURCES = f2fscrypt.c sha512.c
-f2fscrypt_LDFLAGS = -luuid
+f2fscrypt_LDFLAGS = ${libuuid_LIBS}
dist_man_MANS = f2fscrypt.8
endif
diff --git a/tools/check_f2fs.c b/tools/check_f2fs.c
index 579d4dd..74b1460 100644
--- a/tools/check_f2fs.c
+++ b/tools/check_f2fs.c
@@ -23,7 +23,7 @@
#define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6)
#define DB1_PATH "/data/database_file1"
-#define DB2_PATH "/sdcard/Android/data/database_file2"
+#define DB2_PATH "/mnt/androidwritable/0/emulated/0/Android/data/database_file2"
#define FILE_PATH "/data/testfile"
#define BLOCK 4096
@@ -72,7 +72,7 @@ static int test_atomic_write(char *path)
return -1;
}
printf("\tCheck : Atomic in-memory count: 2\n");
- run("cat /sys/kernel/debug/f2fs/status | grep atomic");
+ run("cat /sys/kernel/debug/f2fs/status | grep \"atomic IO\"");
printf("\tCommit ... \n");
ret = ioctl(db, F2FS_IOC_COMMIT_ATOMIC_WRITE);
diff --git a/tools/f2fs_io/Android.bp b/tools/f2fs_io/Android.bp
index 2cc81eb..c89438f 100644
--- a/tools/f2fs_io/Android.bp
+++ b/tools/f2fs_io/Android.bp
@@ -1,8 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_f2fs-tools_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-GPL-2.0
+ // SPDX-license-identifier-LGPL
+ // SPDX-license-identifier-LGPL-2.1
+ default_applicable_licenses: ["external_f2fs-tools_license"],
+}
+
cc_defaults {
name: "f2fs-io-defaults",
cflags: [
"-Wno-unused-function"
],
+ include_dirs: [
+ "external/f2fs-tools/include/",
+ ],
}
cc_binary {
diff --git a/tools/f2fs_io/Makefile.am b/tools/f2fs_io/Makefile.am
index 73ce525..6c17db1 100644
--- a/tools/f2fs_io/Makefile.am
+++ b/tools/f2fs_io/Makefile.am
@@ -1,7 +1,7 @@
## Makefile.am
if LINUX
-AM_CPPFLAGS = -I./include
+AM_CPPFLAGS = -I../../include
AM_CFLAGS = -Wall
sbin_PROGRAMS = f2fs_io
f2fs_io_SOURCES = f2fs_io.c
diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c
index 30544c1..aa1a7e4 100644
--- a/tools/f2fs_io/f2fs_io.c
+++ b/tools/f2fs_io/f2fs_io.c
@@ -24,13 +24,13 @@
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
+#include <linux/fs.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <string.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
@@ -42,6 +42,8 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include <android_config.h>
+
#include "f2fs_io.h"
struct cmd_desc {
@@ -130,6 +132,25 @@ static void full_write(int fd, const void *buf, size_t count)
}
}
+#if defined(__APPLE__)
+static u64 get_current_us()
+{
+#ifdef HAVE_MACH_TIME_H
+ return mach_absolute_time() / 1000;
+#else
+ return 0;
+#endif
+}
+#else
+static u64 get_current_us()
+{
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_BOOTTIME, &t);
+ return (u64)t.tv_sec * 1000000LL + t.tv_nsec / 1000;
+}
+#endif
+
#define fsync_desc "fsync"
#define fsync_help \
"f2fs_io fsync [file]\n\n" \
@@ -192,7 +213,8 @@ static void do_set_verity(int argc, char **argv, const struct cmd_desc *cmd)
" verity\n" \
" casefold\n" \
" compression\n" \
-" nocompression\n"
+" nocompression\n" \
+" immutable\n"
static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
{
@@ -250,6 +272,12 @@ static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
printf("nocow(pinned)");
exist = 1;
}
+ if (flag & FS_IMMUTABLE_FL) {
+ if (exist)
+ printf(",");
+ printf("immutable");
+ exist = 1;
+ }
if (!exist)
printf("none");
printf("\n");
@@ -263,7 +291,8 @@ static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
"flag can be\n" \
" casefold\n" \
" compression\n" \
-" nocompression\n"
+" nocompression\n" \
+" noimmutable\n"
static void do_setflags(int argc, char **argv, const struct cmd_desc *cmd)
{
@@ -289,6 +318,8 @@ static void do_setflags(int argc, char **argv, const struct cmd_desc *cmd)
flag |= FS_COMPR_FL;
else if (!strcmp(argv[1], "nocompression"))
flag |= FS_NOCOMP_FL;
+ else if (!strcmp(argv[1], "noimmutable"))
+ flag &= ~FS_IMMUTABLE_FL;
ret = ioctl(fd, F2FS_IOC_SETFLAGS, &flag);
printf("set a flag on %s ret=%d, flags=%s\n", argv[2], ret, argv[1]);
@@ -349,7 +380,7 @@ static void do_pinfile(int argc, char **argv, const struct cmd_desc *cmd)
exit(1);
}
- fd = xopen(argv[2], O_RDWR, 0);
+ fd = xopen(argv[2], O_RDONLY, 0);
ret = -1;
if (!strcmp(argv[1], "set")) {
@@ -413,6 +444,56 @@ static void do_fallocate(int argc, char **argv, const struct cmd_desc *cmd)
exit(0);
}
+#define erase_desc "erase a block device"
+#define erase_help \
+"f2fs_io erase [block_device_path]\n\n" \
+"Send DISCARD | BLKSECDISCARD comamnd to" \
+"block device in block_device_path\n" \
+
+static void do_erase(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ int fd, ret;
+ struct stat st;
+ u64 range[2];
+
+ if (argc != 2) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ if (stat(argv[1], &st) != 0) {
+ fputs("stat error\n", stderr);
+ exit(1);
+ }
+
+ if (!S_ISBLK(st.st_mode)) {
+ fputs(argv[1], stderr);
+ fputs(" is not a block device\n", stderr);
+ exit(1);
+ }
+
+ fd = xopen(argv[1], O_WRONLY, 0);
+
+ range[0] = 0;
+ ret = ioctl(fd, BLKGETSIZE64, &range[1]);
+ if (ret < 0) {
+ fputs("get size failed\n", stderr);
+ exit(1);
+ }
+
+ ret = ioctl(fd, BLKSECDISCARD, &range);
+ if (ret < 0) {
+ ret = ioctl(fd, BLKDISCARD, &range);
+ if (ret < 0) {
+ fputs("Discard failed\n", stderr);
+ exit(1);
+ }
+ }
+
+ exit(0);
+}
+
#define write_desc "write data into file"
#define write_help \
"f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n" \
@@ -424,6 +505,7 @@ static void do_fallocate(int argc, char **argv, const struct cmd_desc *cmd)
"IO can be\n" \
" buffered : buffered IO\n" \
" dio : direct IO\n" \
+" osync : O_SYNC\n" \
static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
{
@@ -433,6 +515,7 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
unsigned bs, count, i;
int flags = 0;
int fd;
+ u64 total_time = 0, max_time = 0, max_time_t = 0;
srand(time(0));
@@ -460,11 +543,14 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
if (!strcmp(argv[5], "dio"))
flags |= O_DIRECT;
+ else if (!strcmp(argv[5], "osync"))
+ flags |= O_SYNC;
else if (strcmp(argv[5], "buffered"))
die("Wrong IO type");
fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755);
+ total_time = get_current_us();
for (i = 0; i < count; i++) {
if (!strcmp(argv[4], "inc_num"))
*(int *)buf = inc_num++;
@@ -472,13 +558,20 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
*(int *)buf = rand();
/* write data */
+ max_time_t = get_current_us();
ret = pwrite(fd, buf, buf_size, offset + buf_size * i);
+ max_time_t = get_current_us() - max_time_t;
+ if (max_time < max_time_t)
+ max_time = max_time_t;
if (ret != buf_size)
break;
written += ret;
}
- printf("Written %"PRIu64" bytes with pattern=%s\n", written, argv[4]);
+ printf("Written %"PRIu64" bytes with pattern=%s, total_time=%"PRIu64" us, max_latency=%"PRIu64" us\n",
+ written, argv[4],
+ get_current_us() - total_time,
+ max_time);
exit(0);
}
@@ -489,15 +582,18 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
"IO can be\n" \
" buffered : buffered IO\n" \
" dio : direct IO\n" \
+" mmap : mmap IO\n" \
static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
{
u64 buf_size = 0, ret = 0, read_cnt = 0;
u64 offset;
char *buf = NULL;
+ char *data;
char *print_buf = NULL;
unsigned bs, count, i, print_bytes;
int flags = 0;
+ int do_mmap = 0;
int fd;
if (argc != 7) {
@@ -518,6 +614,8 @@ static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
count = atoi(argv[3]);
if (!strcmp(argv[4], "dio"))
flags |= O_DIRECT;
+ else if (!strcmp(argv[4], "mmap"))
+ do_mmap = 1;
else if (strcmp(argv[4], "buffered"))
die("Wrong IO type");
@@ -529,8 +627,20 @@ static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
fd = xopen(argv[6], O_RDONLY | flags, 0);
+ if (do_mmap) {
+ data = mmap(NULL, count * buf_size, PROT_READ,
+ MAP_SHARED, fd, offset);
+ if (data == MAP_FAILED)
+ die("Mmap failed");
+ }
+
for (i = 0; i < count; i++) {
- ret = pread(fd, buf, buf_size, offset + buf_size * i);
+ if (do_mmap) {
+ memcpy(buf, data + offset + buf_size * i, buf_size);
+ ret = buf_size;
+ } else {
+ ret = pread(fd, buf, buf_size, offset + buf_size * i);
+ }
if (ret != buf_size)
break;
@@ -614,27 +724,18 @@ static void do_randread(int argc, char **argv, const struct cmd_desc *cmd)
exit(0);
}
-struct file_ext {
- __u32 f_pos;
- __u32 start_blk;
- __u32 end_blk;
- __u32 blk_count;
-};
-
-#ifndef FIBMAP
-#define FIBMAP _IO(0x00, 1) /* bmap access */
-#endif
-
#define fiemap_desc "get block address in file"
#define fiemap_help \
"f2fs_io fiemap [offset in 4kb] [count] [file_path]\n\n"\
+#if defined(HAVE_LINUX_FIEMAP_H) && defined(HAVE_LINUX_FS_H)
static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
{
- u64 offset;
- u32 blknum;
unsigned count, i;
int fd;
+ __u64 phy_addr;
+ struct fiemap *fm = xmalloc(sizeof(struct fiemap) +
+ sizeof(struct fiemap_extent));
if (argc != 4) {
fputs("Excess arguments\n\n", stderr);
@@ -642,23 +743,37 @@ static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
exit(1);
}
- offset = atoi(argv[1]);
+ fm->fm_start = atoi(argv[1]) * F2FS_BLKSIZE;
+ fm->fm_length = F2FS_BLKSIZE;
+ fm->fm_extent_count = 1;
count = atoi(argv[2]);
fd = xopen(argv[3], O_RDONLY | O_LARGEFILE, 0);
- printf("Fiemap: offset = %08"PRIx64" len = %d\n", offset, count);
+ printf("Fiemap: offset = %08"PRIx64" len = %d\n",
+ (u64)fm->fm_start / F2FS_BLKSIZE, count);
for (i = 0; i < count; i++) {
- blknum = offset + i;
-
- if (ioctl(fd, FIBMAP, &blknum) < 0)
- die_errno("FIBMAP failed");
-
- printf("%u ", blknum);
+ if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
+ die_errno("FIEMAP failed");
+
+ phy_addr = fm->fm_extents[0].fe_physical / F2FS_BLKSIZE;
+ if (phy_addr == NEW_ADDR)
+ printf("NEW_ADDR ");
+ else
+ printf("%llu ", phy_addr);
+ fm->fm_start += F2FS_BLKSIZE;
}
printf("\n");
+ free(fm);
exit(0);
}
+#else
+static void do_fiemap(int UNUSED(argc), char **UNUSED(argv),
+ const struct cmd_desc *UNUSED(cmd))
+{
+ die("Not support for this platform");
+}
+#endif
#define gc_urgent_desc "start/end/run gc_urgent for given time period"
#define gc_urgent_help \
@@ -887,6 +1002,171 @@ static void do_reserve_cblocks(int argc, char **argv, const struct cmd_desc *cmd
exit(0);
}
+#define get_coption_desc "get compression option of a compressed file"
+#define get_coption_help \
+"f2fs_io get_coption [file]\n\n" \
+" algorithm : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n" \
+" log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
+
+static void do_get_coption(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct f2fs_comp_option option;
+ int ret, fd;
+
+ if (argc != 2) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ fd = xopen(argv[1], O_RDONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_GET_COMPRESS_OPTION, &option);
+ if (ret < 0)
+ die_errno("F2FS_IOC_GET_COMPRESS_OPTION failed");
+
+ printf("compression algorithm:%u\n", option.algorithm);
+ printf("compression cluster log size:%u\n", option.log_cluster_size);
+
+ exit(0);
+}
+
+#define set_coption_desc "set compression option of a compressed file"
+#define set_coption_help \
+"f2fs_io set_coption [algorithm] [log_cluster_size] [file_path]\n\n" \
+" algorithm : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n" \
+" log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
+
+static void do_set_coption(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ struct f2fs_comp_option option;
+ int fd, ret;
+
+ if (argc != 4) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ option.algorithm = atoi(argv[1]);
+ option.log_cluster_size = atoi(argv[2]);
+
+ fd = xopen(argv[3], O_WRONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_SET_COMPRESS_OPTION, &option);
+ if (ret < 0)
+ die_errno("F2FS_IOC_SET_COMPRESS_OPTION failed");
+
+ printf("set compression option: algorithm=%u, log_cluster_size=%u\n",
+ option.algorithm, option.log_cluster_size);
+ exit(0);
+}
+
+#define decompress_desc "decompress an already compressed file"
+#define decompress_help "f2fs_io decompress [file_path]\n\n"
+
+static void do_decompress(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ int fd, ret;
+
+ if (argc != 2) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ fd = xopen(argv[1], O_WRONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_DECOMPRESS_FILE);
+ if (ret < 0)
+ die_errno("F2FS_IOC_DECOMPRESS_FILE failed");
+
+ exit(0);
+}
+
+#define compress_desc "compress a compression enabled file"
+#define compress_help "f2fs_io compress [file_path]\n\n"
+
+static void do_compress(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ int fd, ret;
+
+ if (argc != 2) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ fd = xopen(argv[1], O_WRONLY, 0);
+
+ ret = ioctl(fd, F2FS_IOC_COMPRESS_FILE);
+ if (ret < 0)
+ die_errno("F2FS_IOC_COMPRESS_FILE failed");
+
+ exit(0);
+}
+
+#define get_filename_encrypt_mode_desc "get file name encrypt mode"
+#define get_filename_encrypt_mode_help \
+"f2fs_io filename_encrypt_mode [file or directory path]\n\n" \
+"Get the file name encription mode of the given file/directory.\n" \
+
+static void do_get_filename_encrypt_mode (int argc, char **argv,
+ const struct cmd_desc *cmd)
+{
+ static const char *enc_name[] = {
+ "invalid", /* FS_ENCRYPTION_MODE_INVALID (0) */
+ "aes-256-xts", /* FS_ENCRYPTION_MODE_AES_256_XTS (1) */
+ "aes-256-gcm", /* FS_ENCRYPTION_MODE_AES_256_GCM (2) */
+ "aes-256-cbc", /* FS_ENCRYPTION_MODE_AES_256_CBC (3) */
+ "aes-256-cts", /* FS_ENCRYPTION_MODE_AES_256_CTS (4) */
+ "aes-128-cbc", /* FS_ENCRYPTION_MODE_AES_128_CBC (5) */
+ "aes-128-cts", /* FS_ENCRYPTION_MODE_AES_128_CTS (6) */
+ "speck128-256-xts", /* FS_ENCRYPTION_MODE_SPECK128_256_XTS (7) */
+ "speck128-256-cts", /* FS_ENCRYPTION_MODE_SPECK128_256_CTS (8) */
+ "adiantum", /* FS_ENCRYPTION_MODE_ADIANTUM (9) */
+ };
+ int fd, mode, ret;
+ struct fscrypt_get_policy_ex_arg arg;
+
+ if (argc != 2) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ fd = xopen(argv[1], O_RDONLY, 0);
+ arg.policy_size = sizeof(arg.policy);
+ ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg);
+ if (ret != 0 && errno == ENOTTY)
+ ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, arg.policy.v1);
+ close(fd);
+
+ if (ret) {
+ perror("FS_IOC_GET_ENCRYPTION_POLICY|_EX");
+ exit(1);
+ }
+
+ switch (arg.policy.version) {
+ case FSCRYPT_POLICY_V1:
+ mode = arg.policy.v1.filenames_encryption_mode;
+ break;
+ case FSCRYPT_POLICY_V2:
+ mode = arg.policy.v2.filenames_encryption_mode;
+ break;
+ default:
+ printf("Do not support policy version: %d\n",
+ arg.policy.version);
+ exit(1);
+ }
+
+ if (mode >= sizeof(enc_name)/sizeof(enc_name[0])) {
+ printf("Do not support algorithm: %d\n", mode);
+ exit(1);
+ }
+ printf ("%s\n", enc_name[mode]);
+ exit(0);
+}
#define CMD_HIDDEN 0x0001
#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
@@ -902,6 +1182,7 @@ const struct cmd_desc cmd_list[] = {
CMD(shutdown),
CMD(pinfile),
CMD(fallocate),
+ CMD(erase),
CMD(write),
CMD(read),
CMD(randread),
@@ -912,6 +1193,11 @@ const struct cmd_desc cmd_list[] = {
CMD(get_cblocks),
CMD(release_cblocks),
CMD(reserve_cblocks),
+ CMD(get_coption),
+ CMD(set_coption),
+ CMD(decompress),
+ CMD(compress),
+ CMD(get_filename_encrypt_mode),
{ NULL, NULL, NULL, NULL, 0 }
};
diff --git a/tools/f2fs_io/f2fs_io.h b/tools/f2fs_io/f2fs_io.h
index bd19ff9..cdaf00f 100644
--- a/tools/f2fs_io/f2fs_io.h
+++ b/tools/f2fs_io/f2fs_io.h
@@ -10,6 +10,13 @@
#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
#endif
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
#include <sys/types.h>
#ifdef UNUSED
@@ -38,6 +45,9 @@ typedef u16 __be16;
typedef u32 __be32;
#endif
+#define F2FS_BLKSIZE 4096
+#define NEW_ADDR 0xFFFFFFFF
+
#ifndef FS_IOC_GETFLAGS
#define FS_IOC_GETFLAGS _IOR('f', 1, long)
#endif
@@ -74,6 +84,52 @@ typedef u32 __be32;
_IOR(F2FS_IOCTL_MAGIC, 18, __u64)
#define F2FS_IOC_RESERVE_COMPRESS_BLOCKS \
_IOR(F2FS_IOCTL_MAGIC, 19, __u64)
+#define F2FS_IOC_GET_COMPRESS_OPTION _IOR(F2FS_IOCTL_MAGIC, 21, \
+ struct f2fs_comp_option)
+#define F2FS_IOC_SET_COMPRESS_OPTION _IOW(F2FS_IOCTL_MAGIC, 22, \
+ struct f2fs_comp_option)
+#define F2FS_IOC_DECOMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 23)
+#define F2FS_IOC_COMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 24)
+
+#ifndef FSCRYPT_POLICY_V1
+#define FSCRYPT_POLICY_V1 0
+#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
+struct fscrypt_policy_v1 {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+};
+#endif
+#ifndef FS_IOC_GET_ENCRYPTION_POLICY
+#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy_v1)
+#endif
+
+#ifndef FSCRYPT_POLICY_V2
+#define FSCRYPT_POLICY_V2 2
+#define FSCRYPT_KEY_IDENTIFIER_SIZE 16
+struct fscrypt_policy_v2 {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 __reserved[4];
+ __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
+};
+/* Struct passed to FS_IOC_GET_ENCRYPTION_POLICY_EX */
+struct fscrypt_get_policy_ex_arg {
+ __u64 policy_size; /* input/output */
+ union {
+ __u8 version;
+ struct fscrypt_policy_v1 v1;
+ struct fscrypt_policy_v2 v2;
+ } policy; /* output */
+};
+#endif
+#ifndef FS_IOC_GET_ENCRYPTION_POLICY_EX
+#define FS_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 22, __u8[9]) /* size + version */
+#endif
#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
@@ -154,3 +210,8 @@ struct f2fs_flush_device {
u32 dev_num; /* device number to flush */
u32 segments; /* # of segments to flush */
};
+
+struct f2fs_comp_option {
+ u8 algorithm;
+ u8 log_cluster_size;
+};
diff --git a/tools/f2fs_io_parse.c b/tools/f2fs_io_parse.c
index d146ead..47f1194 100644
--- a/tools/f2fs_io_parse.c
+++ b/tools/f2fs_io_parse.c
@@ -271,7 +271,7 @@ static void __print_ftype()
int i;
setlocale(LC_ALL, "");
- printf("\n===== Data R/W in 4KB accoring to File types =====\n");
+ printf("\n===== Data R/W in 4KB according to File types =====\n");
for (i = 0; i < __NR_FILES; i++)
printf(" %17s |", file_type_string[i]);
printf("\n");
diff --git a/tools/f2fscrypt.8 b/tools/f2fscrypt.8
index a60adc8..5e2258a 100644
--- a/tools/f2fscrypt.8
+++ b/tools/f2fscrypt.8
@@ -40,7 +40,7 @@ identifier consisting of 16 hexadecimal characters.
The target directory must be empty.
.SH EXAMPLE
.nf
-Formats a f2fs filesytem that supports encrypt.
+Formats a f2fs filesystem that supports encrypt.
.ft R
# mkfs.f2fs -O encrypt /dev/sdxx
diff --git a/tools/f2fscrypt.c b/tools/f2fscrypt.c
index fe3e0ff..d5bc3c5 100644
--- a/tools/f2fscrypt.c
+++ b/tools/f2fscrypt.c
@@ -43,7 +43,7 @@
#ifdef __KERNEL__
#include <linux/fs.h>
#endif
-#include <uuid/uuid.h>
+#include <uuid.h>
#if !defined(HAVE_ADD_KEY) || !defined(HAVE_KEYCTL)
#include <sys/syscall.h>
diff --git a/tools/sg_write_buffer/Android.bp b/tools/sg_write_buffer/Android.bp
index 56e18dd..93b924d 100644
--- a/tools/sg_write_buffer/Android.bp
+++ b/tools/sg_write_buffer/Android.bp
@@ -1,3 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_f2fs-tools_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-BSD
+ // SPDX-license-identifier-GPL-2.0
+ default_applicable_licenses: ["external_f2fs-tools_license"],
+}
+
cc_defaults {
name: "sg3-utils-defaults",
cflags: [