aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2018-01-10 21:46:59 -0700
committerLinux Build Service Account <lnxbuild@localhost>2018-01-10 21:46:59 -0700
commit6074c18a74751dc1776f864e510af51b374ad2fd (patch)
tree8a6418b35861cddc232bb465b2f1166695b8f19a
parente1553b83ac7109f9c35dfb740aa4957445d3054e (diff)
parentdcbae139f54fef47fa8cd2bbc7baf9de84a65652 (diff)
downloadandroid_external_f2fs-tools-staging/lineage-15.1.tar.gz
android_external_f2fs-tools-staging/lineage-15.1.tar.bz2
android_external_f2fs-tools-staging/lineage-15.1.zip
Merge dcbae139f54fef47fa8cd2bbc7baf9de84a65652 on remote branchstaging/lineage-15.1
Change-Id: I987b250dd09c5350811fcf7e3178574915bbc8a4
-rw-r--r--.gitignore4
-rw-r--r--Android.bp182
-rw-r--r--Android.mk167
-rw-r--r--VERSION4
-rw-r--r--configure.ac101
-rw-r--r--fsck/Makefile.am8
-rw-r--r--fsck/common.h30
-rw-r--r--fsck/dict.c1501
-rw-r--r--fsck/dict.h144
-rw-r--r--fsck/dir.c103
-rw-r--r--fsck/dqblk_v2.h31
-rw-r--r--fsck/dump.c117
-rw-r--r--fsck/f2fs.h19
-rw-r--r--fsck/fsck.c239
-rw-r--r--fsck/fsck.h27
-rw-r--r--fsck/main.c169
-rw-r--r--fsck/mkquota.c404
-rw-r--r--fsck/mount.c259
-rw-r--r--fsck/node.c8
-rw-r--r--fsck/node.h7
-rw-r--r--fsck/quotaio.c221
-rw-r--r--fsck/quotaio.h256
-rw-r--r--fsck/quotaio_tree.c679
-rw-r--r--fsck/quotaio_tree.h70
-rw-r--r--fsck/quotaio_v2.c284
-rw-r--r--fsck/quotaio_v2.h54
-rw-r--r--fsck/resize.c5
-rw-r--r--fsck/segment.c307
-rw-r--r--fsck/sload.c295
-rw-r--r--fsck/xattr.c6
-rw-r--r--fsck/xattr.h74
-rw-r--r--include/android_config.h66
-rw-r--r--include/f2fs_fs.h306
-rw-r--r--include/list.h88
-rw-r--r--include/quota.h86
-rw-r--r--lib/libf2fs.c214
-rw-r--r--lib/libf2fs_io.c246
-rw-r--r--lib/libf2fs_zoned.c2
-rw-r--r--mkfs/Makefile.am4
-rw-r--r--mkfs/f2fs_format.c335
-rw-r--r--mkfs/f2fs_format_main.c45
-rw-r--r--mkfs/f2fs_format_utils.c15
-rw-r--r--tools/Makefile.am6
-rw-r--r--tools/f2fscrypt.c9
-rw-r--r--tools/fibmap.c65
45 files changed, 6528 insertions, 734 deletions
diff --git a/.gitignore b/.gitignore
index abe1336..d5ca55d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,3 +50,7 @@ stamp-h1
/tools/fibmap.f2fs
/tools/parse.f2fs
/tools/f2fscrypt
+
+# cscope files
+cscope.*
+ncscope.*
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..5e462e1
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,182 @@
+// Copyright 2017 The Android Open Source Project
+
+cc_defaults {
+ name: "f2fs-tools-defaults",
+ cflags: [
+ "-DF2FS_MAJOR_VERSION=1",
+ "-DF2FS_MINOR_VERSION=9",
+ "-DF2FS_TOOLS_VERSION=\"1.9.0\"",
+ "-DF2FS_TOOLS_DATE=\"2017-11-13\"",
+ "-DWITH_ANDROID",
+ "-Wall",
+ "-Werror",
+ "-Wno-format",
+ "-Wno-macro-redefined",
+ "-Wno-missing-field-initializers",
+ "-Wno-pointer-arith",
+ "-Wno-sign-compare",
+ "-Wno-unused-function",
+ ],
+ local_include_dirs: [
+ "include",
+ "mkfs",
+ "fsck",
+ ],
+ include_dirs: [
+ "external/e2fsprogs/lib/",
+ "system/core/libsparse/include",
+ ],
+ target: {
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
+cc_defaults {
+ name: "libf2fs_src_files",
+ srcs: [
+ "lib/libf2fs.c",
+ "mkfs/f2fs_format.c",
+ "mkfs/f2fs_format_utils.c",
+ ],
+}
+
+cc_defaults {
+ name: "make_f2fs_src_files",
+ srcs: [
+ "lib/libf2fs_io.c",
+ "mkfs/f2fs_format_main.c",
+ ],
+}
+
+cc_defaults {
+ name: "fsck_main_src_files",
+ srcs: [
+ "fsck/dir.c",
+ "fsck/dict.c",
+ "fsck/mkquota.c",
+ "fsck/quotaio.c",
+ "fsck/quotaio_tree.c",
+ "fsck/quotaio_v2.c",
+ "fsck/node.c",
+ "fsck/segment.c",
+ "fsck/xattr.c",
+ "fsck/main.c",
+ "fsck/mount.c",
+ "lib/libf2fs.c",
+ "lib/libf2fs_io.c",
+ ],
+}
+
+cc_library_static {
+ name: "libf2fs_fmt",
+ defaults: [
+ "f2fs-tools-defaults",
+ "libf2fs_src_files"
+ ],
+}
+
+cc_library_host_static {
+ name: "libf2fs_fmt_host",
+ defaults: [
+ "f2fs-tools-defaults",
+ "libf2fs_src_files"
+ ],
+ target: {
+ windows: {
+ include_dirs: [ "external/e2fsprogs/include/mingw" ],
+ cflags: ["-DANDROID_WINDOWS_HOST"],
+ enabled: true
+ },
+ },
+}
+
+cc_binary {
+ name: "make_f2fs",
+ defaults: [
+ "f2fs-tools-defaults",
+ "make_f2fs_src_files",
+ ],
+ host_supported: true,
+ target: {
+ android: {
+ static_libs: [
+ "libf2fs_fmt",
+ ],
+ shared_libs: [
+ "libext2_uuid",
+ "libsparse",
+ "libbase",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libf2fs_fmt_host",
+ "libext2_uuid",
+ "libsparse",
+ "libbase",
+ "libz",
+ ],
+ },
+ windows: {
+ include_dirs: [ "external/e2fsprogs/include/mingw" ],
+ cflags: ["-DANDROID_WINDOWS_HOST"],
+ ldflags: ["-static"],
+ host_ldlibs: ["-lws2_32"],
+ enabled: true
+ },
+ },
+}
+
+cc_binary {
+ name: "fsck.f2fs",
+ defaults: [
+ "f2fs-tools-defaults",
+ "fsck_main_src_files",
+ ],
+ host_supported: true,
+ srcs: ["fsck/fsck.c"],
+ shared_libs: [
+ "libext2_uuid",
+ "libsparse",
+ "libbase",
+ ],
+}
+
+cc_binary {
+ name: "sload_f2fs",
+ defaults: [
+ "f2fs-tools-defaults",
+ "fsck_main_src_files",
+ ],
+ host_supported: true,
+ cflags: ["-DWITH_SLOAD"],
+ srcs: ["fsck/fsck.c", "fsck/sload.c"],
+ target: {
+ android: {
+ shared_libs: [
+ "libext2_uuid",
+ "libsparse",
+ "libbase",
+ "libcrypto",
+ "libselinux",
+ "libcutils",
+ "liblog",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libext2_uuid",
+ "libsparse",
+ "libbase",
+ "libcrypto",
+ "libselinux",
+ "libcutils",
+ "liblog",
+ "libz",
+ ],
+ },
+ },
+ stl: "libc++_static",
+}
diff --git a/Android.mk b/Android.mk
index 38fce50..108dd99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,63 +1,23 @@
LOCAL_PATH:= $(call my-dir)
# f2fs-tools depends on Linux kernel headers being in the system include path.
-ifeq ($(HOST_OS),linux)
+ifneq (,$filter linux darwin,$(HOST_OS))
# The versions depend on $(LOCAL_PATH)/VERSION
-version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=8 -DF2FS_TOOLS_VERSION=\"1.8.0\" -DF2FS_TOOLS_DATE=\"2017-02-03\"
-common_CFLAGS := -DWITH_ANDROID $(version_CFLAGS)
-# Workaround for the <sys/types.h>/<sys/sysmacros.h> split, here now for
-# bionic and coming later for glibc.
-target_CFLAGS := $(common_CFLAGS) -include sys/sysmacros.h
+version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=9 -DF2FS_TOOLS_VERSION=\"1.9.0\" -DF2FS_TOOLS_DATE=\"2017-11-13\"
+common_CFLAGS := -DWITH_ANDROID $(version_CFLAGS) \
+ -Wall -Werror \
+ -Wno-format \
+ -Wno-macro-redefined \
+ -Wno-missing-field-initializers \
+ -Wno-pointer-arith \
+ -Wno-sign-compare \
+ -Wno-unused-function \
# external/e2fsprogs/lib is needed for uuid/uuid.h
-common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/ system/core/libsparse/include
-
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libf2fs_fmt
-LOCAL_SRC_FILES := \
- lib/libf2fs.c \
- mkfs/f2fs_format.c \
- mkfs/f2fs_format_utils.c \
-
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
-include $(BUILD_STATIC_LIBRARY)
-
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libf2fs_fmt_host
-LOCAL_SRC_FILES := \
- lib/libf2fs.c \
- mkfs/f2fs_format.c \
- mkfs/f2fs_format_utils.c \
-
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(common_CFLAGS)
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libf2fs_fmt_host_dyn
-LOCAL_SRC_FILES := \
- lib/libf2fs.c \
- lib/libf2fs_io.c \
- mkfs/f2fs_format.c \
- mkfs/f2fs_format_utils.c \
-
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(common_CFLAGS)
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/mkfs
-LOCAL_STATIC_LIBRARIES := \
- libf2fs_ioutils_host \
- libext2_uuid \
- libsparse \
- libz
-# LOCAL_LDLIBS := -ldl
-include $(BUILD_HOST_SHARED_LIBRARY)
+common_C_INCLUDES := $(LOCAL_PATH)/include \
+ external/e2fsprogs/lib/ \
+ system/core/libsparse/include \
#----------------------------------------------------------
include $(CLEAR_VARS)
@@ -70,81 +30,54 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := \
- lib/libf2fs_io.c \
- mkfs/f2fs_format_main.c
+ lib/libf2fs_io.c \
+ mkfs/f2fs_format_main.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(target_CFLAGS)
+LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
- libf2fs_fmt \
- libext2_uuid \
- libsparse \
- libz
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
-
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := make_f2fs
-
-LOCAL_SRC_FILES := \
- lib/libf2fs_io.c \
- mkfs/f2fs_format_main.c
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_STATIC_LIBRARIES := libf2fs_fmt
-LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
-LOCAL_SYSTEM_SHARED_LIBRARIES := libc
-LOCAL_MODULE_TAGS := optional
+ libf2fs_fmt \
+ libext2_uuid \
+ libsparse \
+ libz
+LOCAL_WHOLE_STATIC_LIBRARIES := libbase
include $(BUILD_EXECUTABLE)
#----------------------------------------------------------
include $(CLEAR_VARS)
-LOCAL_MODULE := make_f2fs
+# The LOCAL_MODULE name is referenced by the code. Don't change it.
+LOCAL_MODULE := sload.f2fs
-LOCAL_SRC_FILES := \
- mkfs/f2fs_format_main.c \
- lib/libf2fs_io.c \
+# mkfs.f2fs is used in recovery: must be static.
+LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(common_CFLAGS)
-LOCAL_STATIC_LIBRARIES := libf2fs_fmt_host
-LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
-include $(BUILD_HOST_EXECUTABLE)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-# The LOCAL_MODULE name is referenced by the code. Don't change it.
-LOCAL_MODULE := fsck.f2fs
LOCAL_SRC_FILES := \
- fsck/dump.c \
- fsck/fsck.c \
- fsck/main.c \
- fsck/mount.c \
- lib/libf2fs.c \
- lib/libf2fs_io.c \
-
+ fsck/fsck.c \
+ fsck/sload.c \
+ fsck/dir.c \
+ fsck/dict.c \
+ fsck/mkquota.c \
+ fsck/quotaio.c \
+ fsck/quotaio_tree.c \
+ fsck/quotaio_v2.c \
+ fsck/node.c \
+ fsck/segment.c \
+ fsck/xattr.c \
+ fsck/main.c \
+ fsck/mount.c \
+ lib/libf2fs.c \
+ lib/libf2fs_io.c
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(target_CFLAGS)
-LOCAL_SHARED_LIBRARIES := libext2_uuid libsparse
-LOCAL_SYSTEM_SHARED_LIBRARIES := libc
-LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_CFLAGS) -DWITH_SLOAD
+LOCAL_STATIC_LIBRARIES := \
+ libcutils \
+ libselinux \
+ libcrypto \
+ libsparse \
+ liblog \
+ libz
+LOCAL_WHOLE_STATIC_LIBRARIES := libbase
include $(BUILD_EXECUTABLE)
-#----------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := fsck.f2fs
-LOCAL_SRC_FILES := \
- fsck/dump.c \
- fsck/fsck.c \
- fsck/main.c \
- fsck/mount.c \
- lib/libf2fs.c \
- lib/libf2fs_io.c \
-
-LOCAL_C_INCLUDES := $(common_C_INCLUDES)
-LOCAL_CFLAGS := $(common_CFLAGS)
-LOCAL_SHARED_LIBRARIES := libsparse
-LOCAL_HOST_SHARED_LIBRARIES := libext2_uuid
-include $(BUILD_HOST_EXECUTABLE)
-
endif
diff --git a/VERSION b/VERSION
index c520af0..0ceaf97 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-1.8.0
-2017-02-03
+1.9.0
+2017-09-21
diff --git a/configure.ac b/configure.ac
index d6de43b..73c830d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,16 +35,6 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([foreign tar-pax dist-xz])
-AC_CHECK_HEADERS_ONCE([
- fcntl.h
- mntent.h
- stdlib.h
- string.h
- unistd.h
- sys/ioctl.h
- sys/mount.h
-])
-
# Test configure options.
AC_ARG_WITH([selinux],
AS_HELP_STRING([--without-selinux],
@@ -91,8 +81,30 @@ AS_IF([test "x$have_blkid" = "xyes"],
)
# Checks for header files.
-AC_CHECK_HEADERS([linux/fs.h linux/blkzoned.h fcntl.h mntent.h stdlib.h string.h \
- sys/ioctl.h sys/mount.h unistd.h linux/falloc.h byteswap.h])
+AC_CHECK_HEADERS(m4_flatten([
+ attr/xattr.h
+ byteswap.h
+ fcntl.h
+ linux/blkzoned.h
+ linux/falloc.h
+ linux/fs.h
+ linux/hdreg.h
+ linux/limits.h
+ linux/posix_acl.h
+ linux/types.h
+ linux/xattr.h
+ mntent.h
+ scsi/sg.h
+ stdlib.h
+ string.h
+ sys/acl.h
+ sys/ioctl.h
+ sys/syscall.h
+ sys/mount.h
+ sys/sysmacros.h
+ sys/xattr.h
+ unistd.h
+]))
# Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
@@ -103,14 +115,75 @@ AC_TYPE_SIZE_T
# Checks for library functions.
AC_FUNC_GETMNTENT
AC_CHECK_FUNCS_ONCE([
+ add_key
fallocate
+ fsetxattr
+ fstat
+ fstat64
getmntent
+ keyctl
+ llseek
+ lseek64
memset
+ setmntent
])
AS_IF([test "$ac_cv_header_byteswap_h" = "yes"],
[AC_CHECK_DECLS([bswap_64],,,[#include <byteswap.h>])])
+dnl
+dnl Check to see if llseek() is declared in unistd.h. On some libc's
+dnl it is, and on others it isn't..... Thank you glibc developers....
+dnl
+AC_CHECK_DECL(llseek,[AC_DEFINE(HAVE_LLSEEK_PROTOTYPE, 1,
+ [Define to 1 if llseek declared in unistd.h])],,
+ [#include <unistd.h>])
+dnl
+dnl Check to see if lseek64() is declared in unistd.h. Glibc's header files
+dnl are so convoluted that I can't tell whether it will always be defined,
+dnl and if it isn't defined while lseek64 is defined in the library,
+dnl disaster will strike.
+dnl
+dnl Warning! Use of --enable-gcc-wall may throw off this test.
+dnl
+dnl
+AC_CHECK_DECL(lseek64,[AC_DEFINE(HAVE_LSEEK64_PROTOTYPE, 1,
+ [Define to 1 if lseek64 declared in unistd.h])],,
+ [#define _LARGEFILE_SOURCE
+ #define _LARGEFILE64_SOURCE
+ #include <unistd.h>])
+dnl
+dnl Word sizes...
+dnl
+
+# AC_CANONICAL_HOST is needed to access the 'host_os' variable
+AC_CANONICAL_HOST
+
+build_linux=no
+build_windows=no
+build_mac=no
+
+# Detect the target system
+case "${host_os}" in
+linux*)
+ build_linux=yes
+ ;;
+cygwin*|mingw*)
+ build_windows=yes
+ ;;
+darwin*)
+ build_mac=yes
+ ;;
+*)
+ AC_MSG_ERROR(["OS $host_os is not supported"])
+ ;;
+esac
+
+# Pass the conditionals to automake
+AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"])
+AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"])
+AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"])
+
# Install directories
#AC_PREFIX_DEFAULT([/usr])
#AC_SUBST([sbindir], [/sbin])
@@ -126,12 +199,12 @@ AC_CONFIG_FILES([
])
# export library version info for mkfs/libf2fs_format_la
-AC_SUBST(FMT_CURRENT, 1)
+AC_SUBST(FMT_CURRENT, 2)
AC_SUBST(FMT_REVISION, 0)
AC_SUBST(FMT_AGE, 0)
# export library version info for lib/libf2fs_la
-AC_SUBST(LIBF2FS_CURRENT, 2)
+AC_SUBST(LIBF2FS_CURRENT, 3)
AC_SUBST(LIBF2FS_REVISION, 0)
AC_SUBST(LIBF2FS_AGE, 0)
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 7abcd00..1fc7310 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -3,9 +3,11 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
sbin_PROGRAMS = fsck.f2fs
-fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h \
- resize.c \
- node.c segment.c dir.c sload.c xattr.c
+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
+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 \
+ dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c
fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
install-data-hook:
diff --git a/fsck/common.h b/fsck/common.h
new file mode 100644
index 0000000..19a6ecc
--- /dev/null
+++ b/fsck/common.h
@@ -0,0 +1,30 @@
+/**
+ *
+ * Various things common for all utilities
+ *
+ */
+
+#ifndef __QUOTA_COMMON_H__
+#define __QUOTA_COMMON_H__
+
+#undef DEBUG_QUOTA
+
+#ifndef __attribute__
+# if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+# endif
+#endif
+
+#define log_err(format, arg ...) \
+ fprintf(stderr, "[ERROR] %s:%d:%s:: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
+
+#ifdef DEBUG_QUOTA
+# define log_debug(format, arg ...) \
+ fprintf(stderr, "[DEBUG] %s:%d:%s:: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
+#else
+# define log_debug(...)
+#endif
+
+#endif /* __QUOTA_COMMON_H__ */
diff --git a/fsck/dict.c b/fsck/dict.c
new file mode 100644
index 0000000..bb7600c
--- /dev/null
+++ b/fsck/dict.c
@@ -0,0 +1,1501 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#define DICT_NODEBUG
+
+#include "config.h"
+#include <stdlib.h>
+#include <stddef.h>
+#ifdef DICT_NODEBUG
+#define dict_assert(x)
+#else
+#include <assert.h>
+#define dict_assert(x) assert(x)
+#endif
+#define DICT_IMPLEMENTATION
+#include "dict.h"
+#include <f2fs_fs.h>
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $";
+#endif
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define nodecount dict_nodecount
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define allocnode dict_allocnode
+#define freenode dict_freenode
+#define context dict_context
+#define dupes dict_dupes
+
+#define dictptr dict_dictptr
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+#define DICT_DEPTH_MAX 64
+
+static dnode_t *dnode_alloc(void *context);
+static void dnode_free(dnode_t *node, void *context);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree. The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C. The left subtree of C is inherited as the new right subtree
+ * for P. The ordering of the keys within the tree is thus preserved.
+ */
+static void rotate_left(dnode_t *upper)
+{
+ dnode_t *lower, *lowleft, *upparent;
+
+ lower = upper->right;
+ upper->right = lowleft = lower->left;
+ lowleft->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ /* don't need to check for root node here because root->parent is
+ the sentinel nil node, and root->parent->left points back to root */
+
+ if (upper == upparent->left) {
+ upparent->left = lower;
+ } else {
+ dict_assert(upper == upparent->right);
+ upparent->right = lower;
+ }
+
+ lower->left = upper;
+ upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+static void rotate_right(dnode_t *upper)
+{
+ dnode_t *lower, *lowright, *upparent;
+
+ lower = upper->left;
+ upper->left = lowright = lower->right;
+ lowright->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ if (upper == upparent->right) {
+ upparent->right = lower;
+ } else {
+ dict_assert(upper == upparent->left);
+ upparent->left = lower;
+ }
+
+ lower->right = upper;
+ upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it. Used by dict_free().
+ */
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+ if (node == nil)
+ return;
+ free_nodes(dict, node->left, nil);
+ free_nodes(dict, node->right, nil);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * This procedure performs a verification that the given subtree is a binary
+ * search tree. It performs an inorder traversal of the tree using the
+ * dict_next() successor function, verifying that the key of each node is
+ * strictly lower than that of its successor, if duplicates are not allowed,
+ * or lower or equal if duplicates are allowed. This function is used for
+ * debugging purposes.
+ */
+#ifndef DICT_NODEBUG
+static int verify_bintree(dict_t *dict)
+{
+ dnode_t *first, *next;
+
+ first = dict_first(dict);
+
+ if (dict->dupes) {
+ while (first && (next = dict_next(dict, first))) {
+ if (dict->compare(first->key, next->key) > 0)
+ return 0;
+ first = next;
+ }
+ } else {
+ while (first && (next = dict_next(dict, first))) {
+ if (dict->compare(first->key, next->key) >= 0)
+ return 0;
+ first = next;
+ }
+ }
+ return 1;
+}
+
+/*
+ * This function recursively verifies that the given binary subtree satisfies
+ * three of the red black properties. It checks that every red node has only
+ * black children. It makes sure that each node is either red or black. And it
+ * checks that every path has the same count of black nodes from root to leaf.
+ * It returns the blackheight of the given subtree; this allows blackheights to
+ * be computed recursively and compared for left and right siblings for
+ * mismatches. It does not check for every nil node being black, because there
+ * is only one sentinel nil node. The return value of this function is the
+ * black height of the subtree rooted at the node ``root'', or zero if the
+ * subtree is not red-black.
+ */
+static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
+{
+ unsigned height_left, height_right;
+
+ if (root != nil) {
+ height_left = verify_redblack(nil, root->left);
+ height_right = verify_redblack(nil, root->right);
+ if (height_left == 0 || height_right == 0)
+ return 0;
+ if (height_left != height_right)
+ return 0;
+ if (root->color == dnode_red) {
+ if (root->left->color != dnode_black)
+ return 0;
+ if (root->right->color != dnode_black)
+ return 0;
+ return height_left;
+ }
+ if (root->color != dnode_black)
+ return 0;
+ return height_left + 1;
+ }
+ return 1;
+}
+
+/*
+ * Compute the actual count of nodes by traversing the tree and
+ * return it. This could be compared against the stored count to
+ * detect a mismatch.
+ */
+static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
+{
+ if (root == nil)
+ return 0;
+ else
+ return 1 + verify_node_count(nil, root->left)
+ + verify_node_count(nil, root->right);
+}
+#endif
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+ if (root != nil) {
+ return root == node
+ || verify_dict_has_node(nil, root->left, node)
+ || verify_dict_has_node(nil, root->right, node);
+ }
+ return 0;
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Dynamically allocate and initialize a dictionary object.
+ */
+dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
+{
+ dict_t *new = malloc(sizeof *new);
+
+ if (new) {
+ new->compare = comp;
+ new->allocnode = dnode_alloc;
+ new->freenode = dnode_free;
+ new->context = NULL;
+ new->nodecount = 0;
+ new->maxcount = maxcount;
+ new->nilnode.left = &new->nilnode;
+ new->nilnode.right = &new->nilnode;
+ new->nilnode.parent = &new->nilnode;
+ new->nilnode.color = dnode_black;
+ new->dupes = 0;
+ }
+ return new;
+}
+#endif /* FSCK_NOTUSED */
+
+/*
+ * Select a different set of node allocator routines.
+ */
+void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
+ dnode_free_t fr, void *context)
+{
+ dict_assert(dict_count(dict) == 0);
+ dict_assert((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
+
+ dict->allocnode = al ? al : dnode_alloc;
+ dict->freenode = fr ? fr : dnode_free;
+ dict->context = context;
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Free a dynamically allocated dictionary object. Removing the nodes
+ * from the tree before deleting it is required.
+ */
+void dict_destroy(dict_t *dict)
+{
+ dict_assert(dict_isempty(dict));
+ free(dict);
+}
+#endif
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+void dict_free_nodes(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+ free_nodes(dict, root, nil);
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Obsolescent function, equivalent to dict_free_nodes
+ */
+void dict_free(dict_t *dict)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+ dict_assert("call to obsolescent function dict_free()" && 0);
+#endif
+ dict_free_nodes(dict);
+}
+#endif
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+ dict->compare = comp;
+ dict->allocnode = dnode_alloc;
+ dict->freenode = dnode_free;
+ dict->context = NULL;
+ dict->nodecount = 0;
+ dict->maxcount = maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = 0;
+ return dict;
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Initialize a dictionary in the likeness of another dictionary
+ */
+void dict_init_like(dict_t *dict, const dict_t *template)
+{
+ dict->compare = template->compare;
+ dict->allocnode = template->allocnode;
+ dict->freenode = template->freenode;
+ dict->context = template->context;
+ dict->nodecount = 0;
+ dict->maxcount = template->maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = template->dupes;
+
+ dict_assert(dict_similar(dict, template));
+}
+
+/*
+ * Remove all nodes from the dictionary (without freeing them in any way).
+ */
+static void dict_clear(dict_t *dict)
+{
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict_assert(dict->nilnode.color == dnode_black);
+}
+#endif /* FSCK_NOTUSED */
+
+/*
+ * Verify the integrity of the dictionary structure. This is provided for
+ * debugging purposes, and should be placed in assert statements. Just because
+ * this function succeeds doesn't mean that the tree is not corrupt. Certain
+ * corruptions in the tree may simply cause undefined behavior.
+ */
+#ifndef DICT_NODEBUG
+int dict_verify(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+
+ /* check that the sentinel node and root node are black */
+ if (root->color != dnode_black)
+ return 0;
+ if (nil->color != dnode_black)
+ return 0;
+ if (nil->right != nil)
+ return 0;
+ /* nil->left is the root node; check that its parent pointer is nil */
+ if (nil->left->parent != nil)
+ return 0;
+ /* perform a weak test that the tree is a binary search tree */
+ if (!verify_bintree(dict))
+ return 0;
+ /* verify that the tree is a red-black tree */
+ if (!verify_redblack(nil, root))
+ return 0;
+ if (verify_node_count(nil, root) != dict_count(dict))
+ return 0;
+ return 1;
+}
+#endif /* DICT_NODEBUG */
+
+#ifdef FSCK_NOTUSED
+/*
+ * Determine whether two dictionaries are similar: have the same comparison and
+ * allocator functions, and same status as to whether duplicates are allowed.
+ */
+int dict_similar(const dict_t *left, const dict_t *right)
+{
+ if (left->compare != right->compare)
+ return 0;
+
+ if (left->allocnode != right->allocnode)
+ return 0;
+
+ if (left->freenode != right->freenode)
+ return 0;
+
+ if (left->context != right->context)
+ return 0;
+
+ if (left->dupes != right->dupes)
+ return 0;
+
+ return 1;
+}
+#endif /* FSCK_NOTUSED */
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *saved;
+ int result;
+
+ /* simple binary search adapted for trees that contain duplicate keys */
+
+ while (root != nil) {
+ result = dict->compare(key, root->key);
+ if (result < 0)
+ root = root->left;
+ else if (result > 0)
+ root = root->right;
+ else {
+ if (!dict->dupes) { /* no duplicates, return match */
+ return root;
+ } else { /* could be dupes, find leftmost one */
+ do {
+ saved = root;
+ root = root->left;
+ while (root != nil && dict->compare(key, root->key))
+ root = root->right;
+ } while (root != nil);
+ return saved;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Look for the node corresponding to the lowest key that is equal to or
+ * greater than the given key. If there is no such node, return null.
+ */
+dnode_t *dict_lower_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = dict->compare(key, root->key);
+
+ if (result > 0) {
+ root = root->right;
+ } else if (result < 0) {
+ tentative = root;
+ root = root->left;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->left;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Look for the node corresponding to the greatest key that is equal to or
+ * lower than the given key. If there is no such node, return null.
+ */
+dnode_t *dict_upper_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = dict->compare(key, root->key);
+
+ if (result < 0) {
+ root = root->left;
+ } else if (result > 0) {
+ tentative = root;
+ root = root->right;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->right;
+ }
+ }
+ }
+
+ return tentative;
+}
+#endif
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+ dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+ dnode_t *parent = nil, *uncle, *grandpa;
+ int result = -1;
+
+ node->key = key;
+
+ dict_assert(!dict_isfull(dict));
+ dict_assert(!dict_contains(dict, node));
+ dict_assert(!dnode_is_in_a_dict(node));
+
+ /* basic binary tree insert */
+
+ while (where != nil) {
+ parent = where;
+ result = dict->compare(key, where->key);
+ /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+ dict_assert(dict->dupes || result != 0);
+ if (result < 0)
+ where = where->left;
+ else
+ where = where->right;
+ }
+
+ dict_assert(where == nil);
+
+ if (result < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+
+ node->parent = parent;
+ node->left = nil;
+ node->right = nil;
+
+ dict->nodecount++;
+
+ /* red black adjustments */
+
+ node->color = dnode_red;
+
+ while (parent->color == dnode_red) {
+ grandpa = parent->parent;
+ if (parent == grandpa->left) {
+ uncle = grandpa->right;
+ if (uncle->color == dnode_red) { /* red parent, red uncle */
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else { /* red parent, black uncle */
+ if (node == parent->right) {
+ rotate_left(parent);
+ parent = node;
+ dict_assert(grandpa == parent->parent);
+ /* rotation between parent and child preserves grandpa */
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_right(grandpa);
+ break;
+ }
+ } else { /* symmetric cases: parent == parent->parent->right */
+ uncle = grandpa->left;
+ if (uncle->color == dnode_red) {
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else {
+ if (node == parent->left) {
+ rotate_right(parent);
+ parent = node;
+ dict_assert(grandpa == parent->parent);
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_left(grandpa);
+ break;
+ }
+ }
+ }
+
+ dict_root(dict)->color = dnode_black;
+
+ dict_assert(dict_verify(dict));
+}
+
+#ifdef FSCK_NOTUSED
+/*
+ * Delete the given node from the dictionary. If the given node does not belong
+ * to the given dictionary, undefined behavior results. A pointer to the
+ * deleted node is returned.
+ */
+dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
+{
+ dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
+
+ /* basic deletion */
+
+ dict_assert(!dict_isempty(dict));
+ dict_assert(dict_contains(dict, delete));
+
+ /*
+ * If the node being deleted has two children, then we replace it with its
+ * successor (i.e. the leftmost node in the right subtree.) By doing this,
+ * we avoid the traditional algorithm under which the successor's key and
+ * value *only* move to the deleted node and the successor is spliced out
+ * from the tree. We cannot use this approach because the user may hold
+ * pointers to the successor, or nodes may be inextricably tied to some
+ * other structures by way of embedding, etc. So we must splice out the
+ * node we are given, not some other node, and must not move contents from
+ * one node to another behind the user's back.
+ */
+
+ if (delete->left != nil && delete->right != nil) {
+ dnode_t *next = dict_next(dict, delete);
+ dnode_t *nextparent = next->parent;
+ dnode_color_t nextcolor = next->color;
+
+ dict_assert(next != nil);
+ dict_assert(next->parent != nil);
+ dict_assert(next->left == nil);
+
+ /*
+ * First, splice out the successor from the tree completely, by
+ * moving up its right child into its place.
+ */
+
+ child = next->right;
+ child->parent = nextparent;
+
+ if (nextparent->left == next) {
+ nextparent->left = child;
+ } else {
+ dict_assert(nextparent->right == next);
+ nextparent->right = child;
+ }
+
+ /*
+ * Now that the successor has been extricated from the tree, install it
+ * in place of the node that we want deleted.
+ */
+
+ next->parent = delparent;
+ next->left = delete->left;
+ next->right = delete->right;
+ next->left->parent = next;
+ next->right->parent = next;
+ next->color = delete->color;
+ delete->color = nextcolor;
+
+ if (delparent->left == delete) {
+ delparent->left = next;
+ } else {
+ dict_assert(delparent->right == delete);
+ delparent->right = next;
+ }
+
+ } else {
+ dict_assert(delete != nil);
+ dict_assert(delete->left == nil || delete->right == nil);
+
+ child = (delete->left != nil) ? delete->left : delete->right;
+
+ child->parent = delparent = delete->parent;
+
+ if (delete == delparent->left) {
+ delparent->left = child;
+ } else {
+ dict_assert(delete == delparent->right);
+ delparent->right = child;
+ }
+ }
+
+ delete->parent = NULL;
+ delete->right = NULL;
+ delete->left = NULL;
+
+ dict->nodecount--;
+
+ dict_assert(verify_bintree(dict));
+
+ /* red-black adjustments */
+
+ if (delete->color == dnode_black) {
+ dnode_t *parent, *sister;
+
+ dict_root(dict)->color = dnode_red;
+
+ while (child->color == dnode_black) {
+ parent = child->parent;
+ if (child == parent->left) {
+ sister = parent->right;
+ dict_assert(sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_left(parent);
+ sister = parent->right;
+ dict_assert(sister != nil);
+ }
+ if (sister->left->color == dnode_black
+ && sister->right->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->right->color == dnode_black) {
+ dict_assert(sister->left->color == dnode_red);
+ sister->left->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_right(sister);
+ sister = parent->right;
+ dict_assert(sister != nil);
+ }
+ sister->color = parent->color;
+ sister->right->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_left(parent);
+ break;
+ }
+ } else { /* symmetric case: child == child->parent->right */
+ dict_assert(child == parent->right);
+ sister = parent->left;
+ dict_assert(sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_right(parent);
+ sister = parent->left;
+ dict_assert(sister != nil);
+ }
+ if (sister->right->color == dnode_black
+ && sister->left->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->left->color == dnode_black) {
+ dict_assert(sister->right->color == dnode_red);
+ sister->right->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_left(sister);
+ sister = parent->left;
+ dict_assert(sister != nil);
+ }
+ sister->color = parent->color;
+ sister->left->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_right(parent);
+ break;
+ }
+ }
+ }
+
+ child->color = dnode_black;
+ dict_root(dict)->color = dnode_black;
+ }
+
+ dict_assert(dict_verify(dict));
+
+ return delete;
+}
+#endif /* FSCK_NOTUSED */
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+ dnode_t *node = dict->allocnode(dict->context);
+
+ if (node) {
+ dnode_init(node, data);
+ dict_insert(dict, node, key);
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef FSCK_NOTUSED
+void dict_delete_free(dict_t *dict, dnode_t *node)
+{
+ dict_delete(dict, node);
+ dict->freenode(node, dict->context);
+}
+#endif
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+dnode_t *dict_first(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+ if (root != nil)
+ while ((left = root->left) != nil)
+ root = left;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the node with the highest (rightmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+dnode_t *dict_last(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
+
+ if (root != nil)
+ while ((right = root->right) != nil)
+ root = right;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *left;
+
+ if (curr->right != nil) {
+ curr = curr->right;
+ while ((left = curr->left) != nil)
+ curr = left;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->right) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+/*
+ * Return the given node's predecessor, in the key order.
+ * The nil sentinel node is returned if there is no predecessor.
+ */
+dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *right;
+
+ if (curr->left != nil) {
+ curr = curr->left;
+ while ((right = curr->right) != nil)
+ curr = right;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->left) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+void dict_allow_dupes(dict_t *dict)
+{
+ dict->dupes = 1;
+}
+
+#undef dict_count
+#undef dict_isempty
+#undef dict_isfull
+#undef dnode_get
+#undef dnode_put
+#undef dnode_getkey
+
+dictcount_t dict_count(dict_t *dict)
+{
+ return dict->nodecount;
+}
+
+int dict_isempty(dict_t *dict)
+{
+ return dict->nodecount == 0;
+}
+
+int dict_isfull(dict_t *dict)
+{
+ return dict->nodecount == dict->maxcount;
+}
+
+int dict_contains(dict_t *dict, dnode_t *node)
+{
+ return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
+}
+
+static dnode_t *dnode_alloc(void *UNUSED(context))
+{
+ return malloc(sizeof *dnode_alloc(NULL));
+}
+
+static void dnode_free(dnode_t *node, void *UNUSED(context))
+{
+ free(node);
+}
+
+dnode_t *dnode_create(void *data)
+{
+ dnode_t *new = malloc(sizeof *new);
+ if (new) {
+ new->data = data;
+ new->parent = NULL;
+ new->left = NULL;
+ new->right = NULL;
+ }
+ return new;
+}
+
+dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+ dnode->parent = NULL;
+ dnode->left = NULL;
+ dnode->right = NULL;
+ return dnode;
+}
+
+void dnode_destroy(dnode_t *dnode)
+{
+ dict_assert(!dnode_is_in_a_dict(dnode));
+ free(dnode);
+}
+
+void *dnode_get(dnode_t *dnode)
+{
+ return dnode->data;
+}
+
+const void *dnode_getkey(dnode_t *dnode)
+{
+ return dnode->key;
+}
+
+#ifdef FSCK_NOTUSED
+void dnode_put(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+}
+#endif
+
+#ifndef DICT_NODEBUG
+int dnode_is_in_a_dict(dnode_t *dnode)
+{
+ return (dnode->parent && dnode->left && dnode->right);
+}
+#endif
+
+#ifdef FSCK_NOTUSED
+void dict_process(dict_t *dict, void *context, dnode_process_t function)
+{
+ dnode_t *node = dict_first(dict), *next;
+
+ while (node != NULL) {
+ /* check for callback function deleting */
+ /* the next node from under us */
+ dict_assert(dict_contains(dict, node));
+ next = dict_next(dict, node);
+ function(dict, node, context);
+ node = next;
+ }
+}
+
+static void load_begin_internal(dict_load_t *load, dict_t *dict)
+{
+ load->dictptr = dict;
+ load->nilnode.left = &load->nilnode;
+ load->nilnode.right = &load->nilnode;
+}
+
+void dict_load_begin(dict_load_t *load, dict_t *dict)
+{
+ dict_assert(dict_isempty(dict));
+ load_begin_internal(load, dict);
+}
+
+void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *nil = &load->nilnode;
+
+ dict_assert(!dnode_is_in_a_dict(newnode));
+ dict_assert(dict->nodecount < dict->maxcount);
+
+#ifndef DICT_NODEBUG
+ if (dict->nodecount > 0) {
+ if (dict->dupes)
+ dict_assert(dict->compare(nil->left->key, key) <= 0);
+ else
+ dict_assert(dict->compare(nil->left->key, key) < 0);
+ }
+#endif
+
+ newnode->key = key;
+ nil->right->left = newnode;
+ nil->right = newnode;
+ newnode->left = nil;
+ dict->nodecount++;
+}
+
+void dict_load_end(dict_load_t *load)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
+ dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
+ dnode_t *complete = 0;
+ dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
+ dictcount_t botrowcount;
+ unsigned baselevel = 0, level = 0, i;
+
+ dict_assert(dnode_red == 0 && dnode_black == 1);
+
+ while (fullcount >= nodecount && fullcount)
+ fullcount >>= 1;
+
+ botrowcount = nodecount - fullcount;
+
+ for (curr = loadnil->left; curr != loadnil; curr = next) {
+ next = curr->left;
+
+ if (complete == NULL && botrowcount-- == 0) {
+ dict_assert(baselevel == 0);
+ dict_assert(level == 0);
+ baselevel = level = 1;
+ complete = tree[0];
+
+ if (complete != 0) {
+ tree[0] = 0;
+ complete->right = dictnil;
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ }
+ }
+
+ if (complete == NULL) {
+ curr->left = dictnil;
+ curr->right = dictnil;
+ curr->color = level % 2;
+ complete = curr;
+
+ dict_assert(level == baselevel);
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ } else {
+ curr->left = complete;
+ curr->color = (level + 1) % 2;
+ complete->parent = curr;
+ tree[level] = curr;
+ complete = 0;
+ level = baselevel;
+ }
+ }
+
+ if (complete == NULL)
+ complete = dictnil;
+
+ for (i = 0; i < DICT_DEPTH_MAX; i++) {
+ if (tree[i] != 0) {
+ tree[i]->right = complete;
+ complete->parent = tree[i];
+ complete = tree[i];
+ }
+ }
+
+ dictnil->color = dnode_black;
+ dictnil->right = dictnil;
+ complete->parent = dictnil;
+ complete->color = dnode_black;
+ dict_root(dict) = complete;
+
+ dict_assert(dict_verify(dict));
+}
+
+void dict_merge(dict_t *dest, dict_t *source)
+{
+ dict_load_t load;
+ dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
+
+ dict_assert(dict_similar(dest, source));
+
+ if (source == dest)
+ return;
+
+ dest->nodecount = 0;
+ load_begin_internal(&load, dest);
+
+ for (;;) {
+ if (leftnode != NULL && rightnode != NULL) {
+ if (dest->compare(leftnode->key, rightnode->key) < 0)
+ goto copyleft;
+ else
+ goto copyright;
+ } else if (leftnode != NULL) {
+ goto copyleft;
+ } else if (rightnode != NULL) {
+ goto copyright;
+ } else {
+ dict_assert(leftnode == NULL && rightnode == NULL);
+ break;
+ }
+
+copyleft:
+ {
+ dnode_t *next = dict_next(dest, leftnode);
+#ifndef DICT_NODEBUG
+ leftnode->left = NULL; /* suppress assertion in dict_load_next */
+#endif
+ dict_load_next(&load, leftnode, leftnode->key);
+ leftnode = next;
+ continue;
+ }
+
+copyright:
+ {
+ dnode_t *next = dict_next(source, rightnode);
+#ifndef DICT_NODEBUG
+ rightnode->left = NULL;
+#endif
+ dict_load_next(&load, rightnode, rightnode->key);
+ rightnode = next;
+ continue;
+ }
+ }
+
+ dict_clear(source);
+ dict_load_end(&load);
+}
+#endif /* FSCK_NOTUSED */
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+ char **tokptr;
+ va_list arglist;
+ int tokcount = 0;
+
+ va_start(arglist, string);
+ tokptr = va_arg(arglist, char **);
+ while (tokptr) {
+ while (*string && isspace((unsigned char) *string))
+ string++;
+ if (!*string)
+ break;
+ *tokptr = string;
+ while (*string && !isspace((unsigned char) *string))
+ string++;
+ tokptr = va_arg(arglist, char **);
+ tokcount++;
+ if (!*string)
+ break;
+ *string++ = 0;
+ }
+ va_end(arglist);
+
+ return tokcount;
+}
+
+static int comparef(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+static char *dupstring(char *str)
+{
+ int sz = strlen(str) + 1;
+ char *new = malloc(sz);
+ if (new)
+ memcpy(new, str, sz);
+ return new;
+}
+
+static dnode_t *new_node(void *c)
+{
+ static dnode_t few[5];
+ static int count;
+
+ if (count < 5)
+ return few + count++;
+
+ return NULL;
+}
+
+static void del_node(dnode_t *n, void *c)
+{
+}
+
+static int prompt = 0;
+
+static void construct(dict_t *d)
+{
+ input_t in;
+ int done = 0;
+ dict_load_t dl;
+ dnode_t *dn;
+ char *tok1, *tok2, *val;
+ const char *key;
+ char *help =
+ "p turn prompt on\n"
+ "q finish construction\n"
+ "a <key> <val> add new entry\n";
+
+ if (!dict_isempty(d))
+ puts("warning: dictionary not empty!");
+
+ dict_load_begin(&dl, d);
+
+ while (!done) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch (in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 'q':
+ done = 1;
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+ dn = dnode_create(val);
+
+ if (!key || !val || !dn) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ if (dn)
+ dnode_destroy(dn);
+ }
+
+ dict_load_next(&dl, dn, key);
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ dict_load_end(&dl);
+}
+
+int main(void)
+{
+ input_t in;
+ dict_t darray[10];
+ dict_t *d = &darray[0];
+ dnode_t *dn;
+ int i;
+ char *tok1, *tok2, *val;
+ const char *key;
+
+ char *help =
+ "a <key> <val> add value to dictionary\n"
+ "d <key> delete value from dictionary\n"
+ "l <key> lookup value in dictionary\n"
+ "( <key> lookup lower bound\n"
+ ") <key> lookup upper bound\n"
+ "# <num> switch to alternate dictionary (0-9)\n"
+ "j <num> <num> merge two dictionaries\n"
+ "f free the whole dictionary\n"
+ "k allow duplicate keys\n"
+ "c show number of entries\n"
+ "t dump whole dictionary in sort order\n"
+ "m make dictionary out of sorted items\n"
+ "p turn prompt on\n"
+ "s switch to non-functioning allocator\n"
+ "q quit";
+
+ for (i = 0; i < sizeof darray / sizeof *darray; i++)
+ dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
+
+ for (;;) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch(in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+
+ if (!key || !val) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ }
+
+ if (!dict_alloc_insert(d, key, val)) {
+ puts("dict_alloc_insert failed");
+ free((void *) key);
+ free(val);
+ break;
+ }
+ break;
+ case 'd':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = dict_lookup(d, tok1);
+ if (!dn) {
+ puts("dict_lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ key = dnode_getkey(dn);
+ dict_delete_free(d, dn);
+
+ free(val);
+ free((void *) key);
+ break;
+ case 'f':
+ dict_free(d);
+ break;
+ case 'l':
+ case '(':
+ case ')':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = 0;
+ switch (in[0]) {
+ case 'l':
+ dn = dict_lookup(d, tok1);
+ break;
+ case '(':
+ dn = dict_lower_bound(d, tok1);
+ break;
+ case ')':
+ dn = dict_upper_bound(d, tok1);
+ break;
+ }
+ if (!dn) {
+ puts("lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ puts(val);
+ break;
+ case 'm':
+ construct(d);
+ break;
+ case 'k':
+ dict_allow_dupes(d);
+ break;
+ case 'c':
+ printf("%lu\n", (unsigned long) dict_count(d));
+ break;
+ case 't':
+ for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
+ printf("%s\t%s\n", (char *) dnode_getkey(dn),
+ (char *) dnode_get(dn));
+ }
+ break;
+ case 'q':
+ exit(0);
+ break;
+ case '\0':
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 's':
+ dict_set_allocator(d, new_node, del_node, NULL);
+ break;
+ case '#':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ } else {
+ int dictnum = atoi(tok1);
+ if (dictnum < 0 || dictnum > 9) {
+ puts("invalid number");
+ break;
+ }
+ d = &darray[dictnum];
+ }
+ break;
+ case 'j':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ } else {
+ int dict1 = atoi(tok1), dict2 = atoi(tok2);
+ if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
+ puts("invalid number");
+ break;
+ }
+ dict_merge(&darray[dict1], &darray[dict2]);
+ }
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/fsck/dict.h b/fsck/dict.h
new file mode 100644
index 0000000..c59e1a2
--- /dev/null
+++ b/fsck/dict.h
@@ -0,0 +1,144 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <limits.h>
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct dnode_t *dict_left;
+ struct dnode_t *dict_right;
+ struct dnode_t *dict_parent;
+ dnode_color_t dict_color;
+ const void *dict_key;
+ void *dict_data;
+#else
+ int dict_dummy;
+#endif
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *);
+typedef dnode_t *(*dnode_alloc_t)(void *);
+typedef void (*dnode_free_t)(dnode_t *, void *);
+
+typedef struct dict_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dnode_t dict_nilnode;
+ dictcount_t dict_nodecount;
+ dictcount_t dict_maxcount;
+ dict_comp_t dict_compare;
+ dnode_alloc_t dict_allocnode;
+ dnode_free_t dict_freenode;
+ void *dict_context;
+ int dict_dupes;
+#else
+ int dict_dummmy;
+#endif
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dict_t *dict_dictptr;
+ dnode_t dict_nilnode;
+#else
+ int dict_dummmy;
+#endif
+} dict_load_t;
+
+extern dict_t *dict_create(dictcount_t, dict_comp_t);
+extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
+extern void dict_destroy(dict_t *);
+extern void dict_free_nodes(dict_t *);
+extern void dict_free(dict_t *);
+extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
+extern void dict_init_like(dict_t *, const dict_t *);
+extern int dict_verify(dict_t *);
+extern int dict_similar(const dict_t *, const dict_t *);
+extern dnode_t *dict_lookup(dict_t *, const void *);
+extern dnode_t *dict_lower_bound(dict_t *, const void *);
+extern dnode_t *dict_upper_bound(dict_t *, const void *);
+extern void dict_insert(dict_t *, dnode_t *, const void *);
+extern dnode_t *dict_delete(dict_t *, dnode_t *);
+extern int dict_alloc_insert(dict_t *, const void *, void *);
+extern void dict_delete_free(dict_t *, dnode_t *);
+extern dnode_t *dict_first(dict_t *);
+extern dnode_t *dict_last(dict_t *);
+extern dnode_t *dict_next(dict_t *, dnode_t *);
+extern dnode_t *dict_prev(dict_t *, dnode_t *);
+extern dictcount_t dict_count(dict_t *);
+extern int dict_isempty(dict_t *);
+extern int dict_isfull(dict_t *);
+extern int dict_contains(dict_t *, dnode_t *);
+extern void dict_allow_dupes(dict_t *);
+extern int dnode_is_in_a_dict(dnode_t *);
+extern dnode_t *dnode_create(void *);
+extern dnode_t *dnode_init(dnode_t *, void *);
+extern void dnode_destroy(dnode_t *);
+extern void *dnode_get(dnode_t *);
+extern const void *dnode_getkey(dnode_t *);
+extern void dnode_put(dnode_t *, void *);
+extern void dict_process(dict_t *, void *, dnode_process_t);
+extern void dict_load_begin(dict_load_t *, dict_t *);
+extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
+extern void dict_load_end(dict_load_t *);
+extern void dict_merge(dict_t *, dict_t *);
+
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
+#else
+#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
+#endif
+#define dict_count(D) ((D)->dict_nodecount)
+#define dict_isempty(D) ((D)->dict_nodecount == 0)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+#define dnode_put(N, X) ((N)->dict_data = (X))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/fsck/dir.c b/fsck/dir.c
index 57b7f9b..b2ea18f 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -34,20 +34,29 @@ next:
}
-static void make_dentry_ptr(struct f2fs_dentry_ptr *d, void *src, int type)
+void make_dentry_ptr(struct f2fs_dentry_ptr *d, struct f2fs_node *node_blk,
+ void *src, int type)
{
if (type == 1) {
struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
d->max = NR_DENTRY_IN_BLOCK;
+ d->nr_bitmap = SIZE_OF_DENTRY_BITMAP;
d->bitmap = t->dentry_bitmap;
d->dentry = t->dentry;
d->filename = t->filename;
} else {
- struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src;
- d->max = NR_INLINE_DENTRY;
- d->bitmap = t->dentry_bitmap;
- d->dentry = t->dentry;
- d->filename = t->filename;
+ int entry_cnt = NR_INLINE_DENTRY(node_blk);
+ int bitmap_size = INLINE_DENTRY_BITMAP_SIZE(node_blk);
+ int reserved_size = INLINE_RESERVED_SIZE(node_blk);
+
+ d->max = entry_cnt;
+ d->nr_bitmap = bitmap_size;
+ d->bitmap = (u8 *)src;
+ d->dentry = (struct f2fs_dir_entry *)
+ ((char *)src + bitmap_size + reserved_size);
+ d->filename = (__u8 (*)[F2FS_SLOT_LEN])((char *)src +
+ bitmap_size + reserved_size +
+ SIZE_OF_DIR_ENTRY * entry_cnt);
}
}
@@ -61,7 +70,7 @@ static struct f2fs_dir_entry *find_target_dentry(const u8 *name,
if (max_slots)
*max_slots = 0;
- while (bit_pos < d->max) {
+ while (bit_pos < (unsigned long)d->max) {
if (!test_bit_le(bit_pos, d->bitmap)) {
bit_pos++;
max_len++;
@@ -93,7 +102,7 @@ static struct f2fs_dir_entry *find_in_block(void *block,
{
struct f2fs_dentry_ptr d;
- make_dentry_ptr(&d, block, 1);
+ make_dentry_ptr(&d, NULL, block, 1);
return find_target_dentry(name, len, namehash, max_slots, &d);
}
@@ -103,7 +112,7 @@ static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
unsigned int nbucket, nblock;
unsigned int bidx, end_block;
struct f2fs_dir_entry *dentry = NULL;
- struct dnode_of_data dn = {0};
+ struct dnode_of_data dn;
void *dentry_blk;
int max_slots = 214;
nid_t ino = le32_to_cpu(dir->footer.ino);
@@ -122,6 +131,7 @@ static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
dentry_blk = calloc(BLOCK_SZ, 1);
ASSERT(dentry_blk);
+ memset(&dn, 0, sizeof(dn));
for (; bidx < end_block; bidx++) {
/* Firstly, we should know direct node of target data blk */
@@ -199,7 +209,7 @@ static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len);
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dentry_ptr d;
- struct dnode_of_data dn = {0};
+ struct dnode_of_data dn;
nid_t pino = le32_to_cpu(parent->footer.ino);
unsigned int dir_level = parent->i.i_dir_level;
int ret;
@@ -231,6 +241,7 @@ start:
nblock = bucket_blocks(level);
bidx = dir_block_index(level, dir_level, le32_to_cpu(dentry_hash) % nbucket);
+ memset(&dn, 0, sizeof(dn));
for (block = bidx; block <= (bidx + nblock - 1); block++) {
/* Firstly, we should know the direct node of target data blk */
@@ -256,7 +267,7 @@ start:
goto start;
add_dentry:
- make_dentry_ptr(&d, (void *)dentry_blk, 1);
+ make_dentry_ptr(&d, NULL, (void *)dentry_blk, 1);
f2fs_update_dentry(ino, file_type, &d, name, name_len, dentry_hash, bit_pos);
ret = dev_write_block(dentry_blk, dn.data_blkaddr);
@@ -278,7 +289,8 @@ add_dentry:
dn.idirty = 1;
}
- if ((block + 1) * F2FS_BLKSIZE > le64_to_cpu(parent->i.i_size)) {
+ if ((__u64)((block + 1) * F2FS_BLKSIZE) >
+ le64_to_cpu(parent->i.i_size)) {
parent->i.i_size = cpu_to_le64((block + 1) * F2FS_BLKSIZE);
dn.idirty = 1;
}
@@ -307,7 +319,7 @@ static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
nid_t pino = le32_to_cpu(inode->i.i_pino);
struct f2fs_summary sum;
struct node_info ni;
- block_t blkaddr;
+ block_t blkaddr = NULL_ADDR;
int ret;
get_node_info(sbi, ino, &ni);
@@ -336,7 +348,7 @@ static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
ret = dev_write_block(dent_blk, blkaddr);
ASSERT(ret >= 0);
- inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+ inode->i.i_addr[get_extra_isize(inode)] = cpu_to_le32(blkaddr);
free(dent_blk);
}
@@ -347,16 +359,16 @@ static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
struct f2fs_summary sum;
struct node_info ni;
char *data_blk;
- block_t blkaddr;
+ block_t blkaddr = NULL_ADDR;
int ret;
get_node_info(sbi, ino, &ni);
/* store into inline_data */
- if (symlen + 1 <= MAX_INLINE_DATA) {
+ if ((unsigned long)(symlen + 1) <= MAX_INLINE_DATA(inode)) {
inode->i.i_inline |= F2FS_INLINE_DATA;
inode->i.i_inline |= F2FS_DATA_EXIST;
- memcpy(&inode->i.i_addr[1], symname, symlen);
+ memcpy(inline_data_addr(inode), symname, symlen);
return;
}
@@ -371,7 +383,7 @@ static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
ret = dev_write_block(data_blk, blkaddr);
ASSERT(ret >= 0);
- inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+ inode->i.i_addr[get_extra_isize(inode)] = cpu_to_le32(blkaddr);
free(data_blk);
}
@@ -396,7 +408,7 @@ static void init_inode_block(struct f2fs_sb_info *sbi,
ASSERT(de->link);
mode |= S_IFLNK;
size = strlen(de->link);
- if (size + 1 > MAX_INLINE_DATA)
+ if (size + 1 > MAX_INLINE_DATA(node_blk))
blocks++;
} else {
ASSERT(0);
@@ -425,6 +437,12 @@ static void init_inode_block(struct f2fs_sb_info *sbi,
memcpy(node_blk->i.i_name, de->name, de->len);
node_blk->i.i_name[de->len] = 0;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ node_blk->i.i_inline |= F2FS_EXTRA_ATTR;
+ node_blk->i.i_extra_isize =
+ cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE);
+ }
+
node_blk->footer.ino = cpu_to_le32(de->ino);
node_blk->footer.nid = cpu_to_le32(de->ino);
node_blk->footer.flag = 0;
@@ -434,6 +452,10 @@ static void init_inode_block(struct f2fs_sb_info *sbi,
make_empty_dir(sbi, node_blk);
else if (S_ISLNK(mode))
page_symlink(sbi, node_blk, de->link, size);
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ node_blk->i.i_inode_checksum =
+ cpu_to_le32(f2fs_inode_chksum(node_blk));
}
int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
@@ -442,8 +464,8 @@ int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
struct f2fs_inode *inode = &(node->i);
unsigned int dir_level = node->i.i_dir_level;
nid_t ino = le32_to_cpu(node->footer.ino);
- char inline_data[MAX_INLINE_DATA];
- struct dnode_of_data dn = {0};
+ char inline_data[MAX_INLINE_DATA(node)];
+ struct dnode_of_data dn;
struct f2fs_dentry_ptr d;
unsigned long bit_pos = 0;
int ret = 0;
@@ -451,16 +473,17 @@ int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
if (!(inode->i_inline & F2FS_INLINE_DENTRY))
return 0;
- memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA);
- memset(inline_data_addr(node), 0, MAX_INLINE_DATA);
+ memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA(node));
+ memset(inline_data_addr(node), 0, MAX_INLINE_DATA(node));
inode->i_inline &= ~F2FS_INLINE_DENTRY;
ret = dev_write_block(node, p_blkaddr);
ASSERT(ret >= 0);
+ memset(&dn, 0, sizeof(dn));
if (!dir_level) {
- struct f2fs_inline_dentry *inline_dentry;
struct f2fs_dentry_block *dentry_blk;
+ struct f2fs_dentry_ptr src, dst;
dentry_blk = calloc(BLOCK_SZ, 1);
ASSERT(dentry_blk);
@@ -470,17 +493,16 @@ int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
if (dn.data_blkaddr == NULL_ADDR)
new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
- inline_dentry = (struct f2fs_inline_dentry *)inline_data;
+ make_dentry_ptr(&src, node, (void *)inline_data, 2);
+ make_dentry_ptr(&dst, NULL, (void *)dentry_blk, 1);
+
/* copy data from inline dentry block to new dentry block */
- memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
- INLINE_DENTRY_BITMAP_SIZE);
- memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
- SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
+ memcpy(dst.bitmap, src.bitmap, src.nr_bitmap);
+ memset(dst.bitmap + src.nr_bitmap, 0,
+ dst.nr_bitmap - src.nr_bitmap);
- memcpy(dentry_blk->dentry, inline_dentry->dentry,
- sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
- memcpy(dentry_blk->filename, inline_dentry->filename,
- NR_INLINE_DENTRY * F2FS_SLOT_LEN);
+ memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max);
+ memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN);
ret = dev_write_block(dentry_blk, dn.data_blkaddr);
ASSERT(ret >= 0);
@@ -492,9 +514,9 @@ int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node,
}
make_empty_dir(sbi, node);
- make_dentry_ptr(&d, (void *)inline_data, 2);
+ make_dentry_ptr(&d, node, (void *)inline_data, 2);
- while (bit_pos < d.max) {
+ while (bit_pos < (unsigned long)d.max) {
struct f2fs_dir_entry *de;
const unsigned char *filename;
int namelen;
@@ -537,7 +559,7 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
struct f2fs_node *parent, *child;
struct node_info ni;
struct f2fs_summary sum;
- block_t blkaddr;
+ block_t blkaddr = NULL_ADDR;
int ret;
/* Find if there is a */
@@ -598,9 +620,12 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
ASSERT(ret >= 0);
update_free_segments(sbi);
- MSG(1, "Info: Create \"%s\" type=%x, ino=%x / %x into \"%s\"\n",
- de->full_path, de->file_type,
- de->ino, de->pino, de->path);
+ MSG(1, "Info: Create %s -> %s\n"
+ " -- ino=%x, type=%x, mode=%x, uid=%x, "
+ "gid=%x, cap=%"PRIx64", size=%lu, 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);
free_child_dir:
free(child);
free_parent_dir:
diff --git a/fsck/dqblk_v2.h b/fsck/dqblk_v2.h
new file mode 100644
index 0000000..d12512a
--- /dev/null
+++ b/fsck/dqblk_v2.h
@@ -0,0 +1,31 @@
+/*
+ * Header file for disk format of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ */
+
+#ifndef __QUOTA_DQBLK_V2_H__
+#define __QUOTA_DQBLK_V2_H__
+
+#include "quotaio_tree.h"
+
+/* Structure for format specific information */
+struct v2_mem_dqinfo {
+ struct qtree_mem_dqinfo dqi_qtree;
+ unsigned int dqi_flags; /* Flags set in quotafile */
+ unsigned int dqi_used_entries; /* Number of entries in file -
+ updated by scan_dquots */
+ unsigned int dqi_data_blocks; /* Number of data blocks in file -
+ updated by scan_dquots */
+};
+
+struct v2_mem_dqblk {
+ long long dqb_off; /* Offset of dquot in file */
+};
+
+struct quotafile_ops; /* Will be defined later in quotaio.h */
+
+/* Operations above this format */
+extern struct quotafile_ops quotafile_ops_2;
+
+#endif /* __QUOTA_DQBLK_V2_H__ */
diff --git a/fsck/dump.c b/fsck/dump.c
index 22e2265..9f0993e 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -11,6 +11,13 @@
#include <inttypes.h>
#include "fsck.h"
+#include "xattr.h"
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_LINUX_XATTR_H
+#include <linux/xattr.h>
+#endif
#include <locale.h>
#define BUF_SZ 80
@@ -310,26 +317,92 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
free(node_blk);
}
+#ifdef HAVE_FSETXATTR
+static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk)
+{
+ void *xattr;
+ struct f2fs_xattr_entry *ent;
+ char xattr_name[F2FS_NAME_LEN] = {0};
+ int ret;
+
+ xattr = read_all_xattrs(sbi, node_blk);
+
+ list_for_each_xattr(ent, xattr) {
+ char *name = strndup(ent->e_name, ent->e_name_len);
+ void *value = ent->e_name + ent->e_name_len;
+
+ if (!name)
+ continue;
+
+ switch (ent->e_name_index) {
+ case F2FS_XATTR_INDEX_USER:
+ ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
+ XATTR_USER_PREFIX, name);
+ break;
+
+ case F2FS_XATTR_INDEX_SECURITY:
+ ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
+ XATTR_SECURITY_PREFIX, name);
+ break;
+ case F2FS_XATTR_INDEX_TRUSTED:
+ ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
+ XATTR_TRUSTED_PREFIX, name);
+ break;
+ default:
+ MSG(0, "Unknown xattr index 0x%x\n", ent->e_name_index);
+ free(name);
+ continue;
+ }
+ if (ret >= F2FS_NAME_LEN) {
+ MSG(0, "XATTR index 0x%x name too long\n", ent->e_name_index);
+ free(name);
+ continue;
+ }
+
+ DBG(1, "fd %d xattr_name %s\n", c.dump_fd, xattr_name);
+#if defined(__linux__)
+ ret = fsetxattr(c.dump_fd, xattr_name, value,
+ le16_to_cpu(ent->e_value_size), 0);
+#elif defined(__APPLE__)
+ ret = fsetxattr(c.dump_fd, xattr_name, value,
+ le16_to_cpu(ent->e_value_size), 0,
+ XATTR_CREATE);
+#endif
+ if (ret)
+ MSG(0, "XATTR index 0x%x set xattr failed error %d\n",
+ ent->e_name_index, errno);
+
+ free(name);
+ }
+
+ free(xattr);
+}
+#else
+static void dump_xattr(struct f2fs_sb_info *UNUSED(sbi),
+ struct f2fs_node *UNUSED(node_blk))
+{
+ MSG(0, "XATTR does not support\n");
+}
+#endif
+
static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
struct f2fs_node *node_blk)
{
u32 i = 0;
u64 ofs = 0;
- /* TODO: need to dump xattr */
-
- if((node_blk->i.i_inline & F2FS_INLINE_DATA)){
+ if((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
DBG(3, "ino[0x%x] has inline data!\n", nid);
/* recover from inline data */
dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
- 0, MAX_INLINE_DATA);
+ 0, MAX_INLINE_DATA(node_blk));
return;
}
/* check data blocks in inode */
for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
- dump_data_blk(sbi, ofs * F2FS_BLKSIZE,
- le32_to_cpu(node_blk->i.i_addr[i]));
+ dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu(
+ node_blk->i.i_addr[get_extra_isize(node_blk) + i]));
/* check node blocks in inode */
for (i = 0; i < 5; i++) {
@@ -345,6 +418,8 @@ static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
else
ASSERT(0);
}
+
+ dump_xattr(sbi, node_blk);
}
static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
@@ -353,12 +428,17 @@ static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
struct f2fs_inode *inode = &node_blk->i;
u32 imode = le32_to_cpu(inode->i_mode);
u32 namelen = le32_to_cpu(inode->i_namelen);
- unsigned char name[F2FS_NAME_LEN + 1] = {0};
+ char name[F2FS_NAME_LEN + 1] = {0};
char path[1024] = {0};
char ans[255] = {0};
- int enc_name = file_enc_name(inode);
+ int is_encrypted = file_is_encrypt(inode);
int ret;
+ if (is_encrypted) {
+ MSG(force, "File is encrypted\n");
+ return;
+ }
+
if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
MSG(force, "Not a regular file or wrong name info\n\n");
return;
@@ -376,8 +456,7 @@ dump:
ASSERT(ret >= 0);
/* make a file */
- namelen = convert_encrypted_name(inode->i_name, namelen,
- name, enc_name);
+ strncpy(name, (const char *)inode->i_name, namelen);
name[namelen] = 0;
sprintf(path, "./lost_found/%s", name);
@@ -419,17 +498,17 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
le32_to_cpu(node_blk->footer.nid) == ni.nid &&
ni.ino == ni.nid) {
- print_node_info(node_blk, force);
+ print_node_info(sbi, node_blk, force);
dump_file(sbi, &ni, node_blk, force);
} else {
- print_node_info(node_blk, force);
+ print_node_info(sbi, node_blk, force);
MSG(force, "Invalid (i)node block\n\n");
}
free(node_blk);
}
-static void dump_node_from_blkaddr(u32 blk_addr)
+static void dump_node_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
{
struct f2fs_node *node_blk;
int ret;
@@ -441,9 +520,9 @@ static void dump_node_from_blkaddr(u32 blk_addr)
ASSERT(ret >= 0);
if (c.dbg_lv > 0)
- print_node_info(node_blk, 0);
+ print_node_info(sbi, node_blk, 0);
else
- print_inode_info(&node_blk->i, 1);
+ print_inode_info(sbi, node_blk, 1);
free(node_blk);
}
@@ -567,7 +646,7 @@ int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
/* print inode */
if (c.dbg_lv > 0)
- dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
@@ -575,7 +654,7 @@ int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
nid, ni.blk_addr);
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
- dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
dump_data_offset(ni.blk_addr,
le16_to_cpu(sum_entry.ofs_in_node));
} else {
@@ -583,13 +662,13 @@ int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
if (ni.ino == ni.nid) {
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
- dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
} else {
MSG(0, " - Node block : id = 0x%x from 0x%x\n",
nid, ni.blk_addr);
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
- dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
dump_node_offset(ni.blk_addr);
}
}
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index efc43f6..34b2481 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -11,6 +11,7 @@
#ifndef _F2FS_H_
#define _F2FS_H_
+#include <f2fs_fs.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
@@ -18,14 +19,14 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
+#ifdef HAVE_MNTENT_H
#include <mntent.h>
+#endif
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <assert.h>
-#include <f2fs_fs.h>
-
#define EXIT_ERR_CODE (-1)
#define ver_after(a, b) (typecheck(unsigned long long, a) && \
typecheck(unsigned long long, b) && \
@@ -129,6 +130,7 @@ struct f2fs_dentry_ptr {
struct f2fs_dir_entry *dentry;
__u8 (*filename)[F2FS_SLOT_LEN];
int max;
+ int nr_bitmap;
};
struct dentry {
@@ -232,7 +234,9 @@ static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi)
static inline void *inline_data_addr(struct f2fs_node *node_blk)
{
- return (void *)&(node_blk->i.i_addr[1]);
+ int ofs = get_extra_isize(node_blk) + DEF_INLINE_RESERVED_SIZE;
+
+ return (void *)&(node_blk->i.i_addr[ofs]);
}
static inline unsigned int ofs_of_node(struct f2fs_node *node_blk)
@@ -273,7 +277,7 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
{
unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags);
- return ckpt_flags & f;
+ return ckpt_flags & f ? 1 : 0;
}
static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
@@ -451,14 +455,13 @@ static inline int map_de_type(umode_t mode)
static inline void *inline_xattr_addr(struct f2fs_inode *inode)
{
- return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE_INLINE_XATTR]);
+ return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE -
+ get_inline_xattr_addrs(inode)]);
}
static inline int inline_xattr_size(struct f2fs_inode *inode)
{
- if (inode->i_inline & F2FS_INLINE_XATTR)
- return F2FS_INLINE_XATTR_ADDRS << 2;
- return 0;
+ return get_inline_xattr_addrs(inode) * sizeof(__le32);
}
extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne);
diff --git a/fsck/fsck.c b/fsck/fsck.c
index 56336ad..11b8b0b 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -9,12 +9,12 @@
* published by the Free Software Foundation.
*/
#include "fsck.h"
+#include "quotaio.h"
char *tree_mark;
uint32_t tree_mark_size = 256;
-static inline int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk,
- int type)
+int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk, int type)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct seg_entry *se;
@@ -50,6 +50,13 @@ static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap);
}
+int f2fs_set_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+
+ return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap);
+}
+
static int add_into_hard_link_list(struct f2fs_sb_info *sbi,
u32 nid, u32 link_cnt)
{
@@ -226,10 +233,13 @@ static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
goto out;
/* check its block address */
- if (node_blk->footer.nid == node_blk->footer.ino)
- target_blk_addr = node_blk->i.i_addr[ofs_in_node];
- else
+ if (node_blk->footer.nid == node_blk->footer.ino) {
+ int ofs = get_extra_isize(node_blk);
+
+ target_blk_addr = node_blk->i.i_addr[ofs + ofs_in_node];
+ } else {
target_blk_addr = node_blk->dn.addr[ofs_in_node];
+ }
if (blk_addr == le32_to_cpu(target_blk_addr))
ret = 1;
@@ -455,6 +465,25 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
return 0;
}
+static int sanity_check_inode(struct f2fs_sb_info *sbi, struct f2fs_node *node)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_inode *fi = &node->i;
+
+ if (!(le16_to_cpu(fi->i_mode) & S_IFMT)) {
+ ASSERT_MSG("i_mode is not valid. [0x%x]", le16_to_cpu(fi->i_mode));
+ goto remove_node;
+ }
+
+ return 0;
+
+remove_node:
+ f2fs_set_bit(le32_to_cpu(node->footer.ino), fsck->nat_area_bitmap);
+ fsck->chk.valid_blk_cnt--;
+ fsck->chk.valid_node_cnt--;
+ return -EINVAL;
+}
+
static int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino,
u32 x_nid, u32 *blk_cnt)
{
@@ -497,7 +526,12 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
goto err;
if (ntype == TYPE_INODE) {
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+
+ if (sanity_check_inode(sbi, node_blk))
+ goto err;
fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni);
+ quota_add_inode_usage(fsck->qctx, nid, &node_blk->i);
} else {
switch (ntype) {
case TYPE_DIRECT_NODE:
@@ -598,7 +632,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
u32 i_links = le32_to_cpu(node_blk->i.i_links);
u64 i_size = le64_to_cpu(node_blk->i.i_size);
u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
- unsigned char en[F2FS_NAME_LEN + 1];
+ int ofs = get_extra_isize(node_blk);
+ unsigned char *en;
int namelen;
unsigned int idx = 0;
int need_fix = 0;
@@ -619,7 +654,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
f2fs_set_main_bitmap(sbi, ni->blk_addr,
CURSEG_WARM_NODE);
- if (i_links > 1 && ftype != F2FS_FT_ORPHAN) {
+ if (i_links > 1 && ftype != F2FS_FT_ORPHAN &&
+ !is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) {
/* First time. Create new hard link node */
add_into_hard_link_list(sbi, nid, i_links);
fsck->chk.multi_hard_link_files++;
@@ -657,21 +693,21 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
ftype == F2FS_FT_FIFO || ftype == F2FS_FT_SOCK)
goto check;
- if((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
- if (le32_to_cpu(node_blk->i.i_addr[0]) != 0) {
+ if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
+ if (le32_to_cpu(node_blk->i.i_addr[ofs]) != 0) {
/* should fix this bug all the time */
FIX_MSG("inline_data has wrong 0'th block = %x",
- le32_to_cpu(node_blk->i.i_addr[0]));
- node_blk->i.i_addr[0] = 0;
+ le32_to_cpu(node_blk->i.i_addr[ofs]));
+ node_blk->i.i_addr[ofs] = 0;
node_blk->i.i_blocks = cpu_to_le64(*blk_cnt);
need_fix = 1;
}
if (!(node_blk->i.i_inline & F2FS_DATA_EXIST)) {
- char buf[MAX_INLINE_DATA];
- memset(buf, 0, MAX_INLINE_DATA);
+ char buf[MAX_INLINE_DATA(node_blk)];
+ memset(buf, 0, MAX_INLINE_DATA(node_blk));
- if (memcmp(buf, &node_blk->i.i_addr[1],
- MAX_INLINE_DATA)) {
+ if (memcmp(buf, inline_data_addr(node_blk),
+ MAX_INLINE_DATA(node_blk))) {
FIX_MSG("inline_data has DATA_EXIST");
node_blk->i.i_inline |= F2FS_DATA_EXIST;
need_fix = 1;
@@ -680,8 +716,18 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
DBG(3, "ino[0x%x] has inline data!\n", nid);
goto check;
}
- if((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) {
+
+ if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) {
DBG(3, "ino[0x%x] has inline dentry!\n", nid);
+ if (le32_to_cpu(node_blk->i.i_addr[ofs]) != 0) {
+ /* should fix this bug all the time */
+ FIX_MSG("inline_dentry has wrong 0'th block = %x",
+ le32_to_cpu(node_blk->i.i_addr[ofs]));
+ node_blk->i.i_addr[ofs] = 0;
+ node_blk->i.i_blocks = cpu_to_le64(*blk_cnt);
+ need_fix = 1;
+ }
+
ret = fsck_chk_inline_dentries(sbi, node_blk, &child);
if (ret < 0) {
/* should fix this bug all the time */
@@ -710,7 +756,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
/* check data blocks in inode */
for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i);
idx++, child.pgofs++) {
- block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[idx]);
+ block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]);
/* check extent info */
check_extent_info(&child, blkaddr, 0);
@@ -724,9 +770,10 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
if (!ret) {
*blk_cnt = *blk_cnt + 1;
} else if (c.fix_on) {
- node_blk->i.i_addr[idx] = 0;
+ node_blk->i.i_addr[ofs + idx] = 0;
need_fix = 1;
- FIX_MSG("[0x%x] i_addr[%d] = 0", nid, idx);
+ FIX_MSG("[0x%x] i_addr[%d] = 0",
+ nid, ofs + idx);
}
}
}
@@ -791,6 +838,9 @@ check:
}
}
skip_blkcnt_fix:
+ en = malloc(F2FS_NAME_LEN + 1);
+ ASSERT(en);
+
namelen = convert_encrypted_name(node_blk->i.i_name,
le32_to_cpu(node_blk->i.i_namelen),
en, file_enc_name(&node_blk->i));
@@ -800,6 +850,11 @@ skip_blkcnt_fix:
le32_to_cpu(node_blk->footer.ino),
en, (u32)i_blocks);
+ if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid))
+ DBG(1, "Quota Inode: 0x%x [%s] i_blocks: %u\n\n",
+ le32_to_cpu(node_blk->footer.ino),
+ en, (u32)i_blocks);
+
if (ftype == F2FS_FT_DIR) {
DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
le32_to_cpu(node_blk->footer.ino), en,
@@ -827,16 +882,19 @@ skip_blkcnt_fix:
}
}
}
+
+ free(en);
+
if (ftype == F2FS_FT_SYMLINK && i_blocks && i_size == 0) {
DBG(1, "ino: 0x%x i_blocks: %lu with zero i_size",
- nid, i_blocks);
+ nid, (unsigned long)i_blocks);
if (c.fix_on) {
u64 i_size = i_blocks * F2FS_BLKSIZE;
node_blk->i.i_size = cpu_to_le64(i_size);
need_fix = 1;
FIX_MSG("Symlink: recover 0x%x with i_size=%lu",
- nid, i_size);
+ nid, (unsigned long)i_size);
}
}
@@ -850,9 +908,32 @@ skip_blkcnt_fix:
nid, i_links);
}
}
- if (need_fix && !c.ro) {
- /* drop extent information to avoid potential wrong access */
+
+ /* drop extent information to avoid potential wrong access */
+ if (need_fix && !c.ro)
node_blk->i.i_ext.len = 0;
+
+ if ((c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) &&
+ f2fs_has_extra_isize(&node_blk->i)) {
+ __u32 provided, calculated;
+
+ provided = le32_to_cpu(node_blk->i.i_inode_checksum);
+ calculated = f2fs_inode_chksum(node_blk);
+
+ if (provided != calculated) {
+ ASSERT_MSG("ino: 0x%x chksum:0x%x, but calculated one is: 0x%x",
+ nid, provided, calculated);
+ if (c.fix_on) {
+ node_blk->i.i_inode_checksum =
+ cpu_to_le32(calculated);
+ need_fix = 1;
+ FIX_MSG("ino: 0x%x recover, i_inode_checksum= 0x%x -> 0x%x",
+ nid, provided, calculated);
+ }
+ }
+ }
+
+ if (need_fix && !c.ro) {
ret = dev_write_block(node_blk, ni->blk_addr);
ASSERT(ret >= 0);
}
@@ -915,7 +996,7 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
else {
node_blk->in.nid[i] = 0;
need_fix = 1;
- FIX_MSG("Set indirect node 0x%x -> 0\n", i);
+ FIX_MSG("Set indirect node 0x%x -> 0", i);
}
skip:
child->pgofs += ADDRS_PER_BLOCK;
@@ -955,7 +1036,7 @@ int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
else {
node_blk->in.nid[i] = 0;
need_fix = 1;
- FIX_MSG("Set double indirect node 0x%x -> 0\n", i);
+ FIX_MSG("Set double indirect node 0x%x -> 0", i);
}
skip:
child->pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
@@ -1355,17 +1436,18 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
struct f2fs_node *node_blk, struct child_info *child)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
- struct f2fs_inline_dentry *de_blk;
+ struct f2fs_dentry_ptr d;
+ void *inline_dentry;
int dentries;
- de_blk = inline_data_addr(node_blk);
- ASSERT(de_blk != NULL);
+ inline_dentry = inline_data_addr(node_blk);
+ ASSERT(inline_dentry != NULL);
+
+ make_dentry_ptr(&d, node_blk, inline_dentry, 2);
fsck->dentry_depth++;
dentries = __chk_dentries(sbi, child,
- de_blk->dentry_bitmap,
- de_blk->dentry, de_blk->filename,
- NR_INLINE_DENTRY, 1,
+ d.bitmap, d.dentry, d.filename, d.max, 1,
file_enc_name(&node_blk->i));
if (dentries < 0) {
DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
@@ -1374,7 +1456,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
DBG(1, "[%3d] Inline Dentry Block Done : "
"dentries:%d in %d slots (len:%d)\n\n",
fsck->dentry_depth, dentries,
- (int)NR_INLINE_DENTRY, F2FS_NAME_LEN);
+ d.max, F2FS_NAME_LEN);
}
fsck->dentry_depth--;
return dentries;
@@ -1527,6 +1609,83 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
return 0;
}
+int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ enum quota_type qtype;
+ int ret = 0;
+ u32 blk_cnt = 0;
+
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ nid_t ino = QUOTA_INO(sb, qtype);
+ struct node_info ni;
+
+ DBG(1, "[%3d] ino [0x%x]\n", qtype, ino);
+ blk_cnt = 1;
+
+ if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
+ get_node_info(sbi, ino, &ni);
+ if (!IS_VALID_NID(sbi, ino) ||
+ !IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+ return -EINVAL;
+ }
+ ret = fsck_chk_node_blk(sbi, NULL, ino,
+ F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL);
+ if (ret)
+ ASSERT_MSG("[0x%x] wrong orphan inode", ino);
+ }
+ return ret;
+}
+
+int fsck_chk_quota_files(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ enum quota_type qtype;
+ f2fs_ino_t ino;
+ int ret = 0;
+ int needs_writeout;
+
+ /* Return if quota feature is disabled */
+ if (!fsck->qctx)
+ return 0;
+
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ ino = sb->qf_ino[qtype];
+ if (!ino)
+ continue;
+
+ DBG(1, "Checking Quota file ([%3d] ino [0x%x])\n", qtype, ino);
+ needs_writeout = 0;
+ ret = quota_compare_and_update(sbi, qtype, &needs_writeout,
+ c.preserve_limits);
+ if (ret == 0 && needs_writeout == 0) {
+ DBG(1, "OK\n");
+ continue;
+ }
+
+ /* Something is wrong */
+ if (c.fix_on) {
+ DBG(0, "Fixing Quota file ([%3d] ino [0x%x])\n",
+ qtype, ino);
+ f2fs_filesize_update(sbi, ino, 0);
+ ret = quota_write_inode(sbi, qtype);
+ if (!ret) {
+ c.bug_on = 1;
+ DBG(1, "OK\n");
+ } else {
+ ASSERT_MSG("Unable to write quota file");
+ }
+ } else {
+ ASSERT_MSG("Quota file is missing or invalid"
+ " quota file content found.");
+ }
+ }
+ return ret;
+}
+
int fsck_chk_meta(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@@ -1587,6 +1746,10 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi)
if (fsck_chk_orphan_node(sbi))
return -EINVAL;
+ /* 5. check quota inode simply */
+ if (fsck_chk_quota_node(sbi))
+ return -EINVAL;
+
if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) {
ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u,"
" valid_inode_count %u",
@@ -1995,9 +2158,11 @@ int fsck_verify(struct f2fs_sb_info *sbi)
fix_hard_links(sbi);
fix_nat_entries(sbi);
rewrite_sit_area_bitmap(sbi);
- move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
- write_curseg_info(sbi);
- flush_curseg_sit_entries(sbi);
+ if (check_curseg_offset(sbi)) {
+ move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+ write_curseg_info(sbi);
+ flush_curseg_sit_entries(sbi);
+ }
fix_checkpoint(sbi);
} else if (is_set_ckpt_flags(cp, CP_FSCK_FLAG)) {
write_checkpoint(sbi);
@@ -2009,6 +2174,10 @@ int fsck_verify(struct f2fs_sb_info *sbi)
void fsck_free(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+
+ if (fsck->qctx)
+ quota_release_context(&fsck->qctx);
+
if (fsck->main_area_bitmap)
free(fsck->main_area_bitmap);
diff --git a/fsck/fsck.h b/fsck/fsck.h
index c54eccb..0343fbd 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -13,6 +13,8 @@
#include "f2fs.h"
+struct quota_ctx;
+
#define FSCK_UNMATCHED_EXTENT 0x00000001
enum {
@@ -85,6 +87,8 @@ struct f2fs_fsck {
u32 dentry_depth;
struct f2fs_nat_entry *entries;
u32 nat_valid_inode_cnt;
+
+ struct quota_ctx *qctx;
};
#define BLOCK_SZ 4096
@@ -118,6 +122,8 @@ enum seg_type {
struct selabel_handle;
extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
+extern int fsck_chk_quota_node(struct f2fs_sb_info *);
+extern int fsck_chk_quota_files(struct f2fs_sb_info *);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
enum FILE_TYPE, enum NODE_TYPE, u32 *,
struct child_info *);
@@ -141,8 +147,8 @@ int convert_encrypted_name(unsigned char *, int, unsigned char *, int);
extern void update_free_segments(struct f2fs_sb_info *);
void print_cp_state(u32);
-extern void print_node_info(struct f2fs_node *, int);
-extern void print_inode_info(struct f2fs_inode *, int);
+extern void print_node_info(struct f2fs_sb_info *, struct f2fs_node *, int);
+extern void print_inode_info(struct f2fs_sb_info *, struct f2fs_node *, int);
extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int);
extern struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *,
unsigned int, int *);
@@ -154,6 +160,8 @@ extern void nullify_nat_entry(struct f2fs_sb_info *, u32);
extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *);
extern void build_nat_area_bitmap(struct f2fs_sb_info *);
extern void build_sit_area_bitmap(struct f2fs_sb_info *);
+extern int f2fs_set_main_bitmap(struct f2fs_sb_info *, u32, int);
+extern int f2fs_set_sit_bitmap(struct f2fs_sb_info *, u32);
extern void fsck_init(struct f2fs_sb_info *);
extern int fsck_verify(struct f2fs_sb_info *);
extern void fsck_free(struct f2fs_sb_info *);
@@ -208,8 +216,9 @@ int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
int f2fs_resize(struct f2fs_sb_info *);
/* sload.c */
-int f2fs_sload(struct f2fs_sb_info *, const char *, const char *,
- const char *, struct selabel_handle *);
+int f2fs_sload(struct f2fs_sb_info *);
+
+/* segment.c */
void reserve_new_block(struct f2fs_sb_info *, block_t *,
struct f2fs_summary *, int);
void new_data_block(struct f2fs_sb_info *, void *,
@@ -219,12 +228,22 @@ void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *, int);
void set_data_blkaddr(struct dnode_of_data *);
block_t new_node_block(struct f2fs_sb_info *,
struct dnode_of_data *, unsigned int);
+
+/* segment.c */
+u64 f2fs_read(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t);
+u64 f2fs_write(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t);
+void f2fs_filesize_update(struct f2fs_sb_info *, nid_t, u64);
+
void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
pgoff_t, int);
+void make_dentry_ptr(struct f2fs_dentry_ptr *, struct f2fs_node *, void *, int);
int f2fs_create(struct f2fs_sb_info *, struct dentry *);
int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *);
int f2fs_symlink(struct f2fs_sb_info *, struct dentry *);
int inode_set_selinux(struct f2fs_sb_info *, u32, const char *);
int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
+/* xattr.c */
+void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *);
+
#endif /* _FSCK_H_ */
diff --git a/fsck/main.c b/fsck/main.c
index c9411eb..db5c413 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -18,9 +18,34 @@
#include "fsck.h"
#include <libgen.h>
#include <ctype.h>
+#include <getopt.h>
+#include "quotaio.h"
struct f2fs_fsck gfsck;
+#ifdef WITH_ANDROID
+#include <sparse/sparse.h>
+extern struct sparse_file *f2fs_sparse_file;
+#endif
+
+static char *absolute_path(const char *file)
+{
+ char *ret;
+ char cwd[PATH_MAX];
+
+ if (file[0] != '/') {
+ if (getcwd(cwd, PATH_MAX) == NULL) {
+ fprintf(stderr, "Failed to getcwd\n");
+ exit(EXIT_FAILURE);
+ }
+ ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
+ if (ret)
+ sprintf(ret, "%s/%s", cwd, file);
+ } else
+ ret = strdup(file);
+ return ret;
+}
+
void fsck_usage()
{
MSG(0, "\nUsage: fsck.f2fs [options] device\n");
@@ -29,7 +54,10 @@ void fsck_usage()
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -f check/fix entire partition\n");
MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n");
+ MSG(0, " -S sparse_mode\n");
MSG(0, " -t show directory tree\n");
+ MSG(0, " -q preserve quota limits\n");
+ MSG(0, " --dry-run do not really fix corruptions\n");
exit(1);
}
@@ -41,6 +69,7 @@ void dump_usage()
MSG(0, " -i inode no (hex)\n");
MSG(0, " -n [NAT dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
+ MSG(0, " -S sparse_mode\n");
MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -b blk_addr (in 4KB)\n");
@@ -53,6 +82,7 @@ void defrag_usage()
MSG(0, "[options]:\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -s start block address [default: main_blkaddr]\n");
+ MSG(0, " -S sparse_mode\n");
MSG(0, " -l length [default:512 (2MB)]\n");
MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n");
MSG(0, " -i set direction as shrink [default: expand]\n");
@@ -72,8 +102,13 @@ void sload_usage()
{
MSG(0, "\nUsage: sload.f2fs [options] device\n");
MSG(0, "[options]:\n");
+ MSG(0, " -C fs_config\n");
MSG(0, " -f source directory [path of the source directory]\n");
+ MSG(0, " -p product out directory\n");
+ MSG(0, " -s file_contexts\n");
+ 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, " -d debug level [default:0]\n");
exit(1);
}
@@ -109,18 +144,36 @@ void f2fs_parse_options(int argc, char *argv[])
int option = 0;
char *prog = basename(argv[0]);
int err = NOERROR;
+#ifdef WITH_ANDROID
+ int i;
+ /* Allow prog names (e.g, sload_f2fs, fsck_f2fs, etc) */
+ for (i = 0; i < strlen(prog); i++) {
+ if (prog[i] == '_')
+ prog[i] = '.';
+ }
+#endif
if (argc < 2) {
MSG(0, "\tError: Device not specified\n");
error_out(prog);
}
if (!strcmp("fsck.f2fs", prog)) {
- const char *option_string = ":ad:fp:t";
+ const char *option_string = ":ad:fp:q:St";
+ int opt = 0;
+ struct option long_opt[] = {
+ {"dry-run", no_argument, 0, 1},
+ {0, 0, 0, 0}
+ };
c.func = FSCK;
- while ((option = getopt(argc, argv, option_string)) != EOF) {
+ while ((option = getopt_long(argc, argv, option_string,
+ long_opt, &opt)) != EOF) {
switch (option) {
+ case 1:
+ c.dry_run = 1;
+ MSG(0, "Info: Dry run\n");
+ break;
case 'a':
c.auto_fix = 1;
MSG(0, "Info: Fix the reported corruption.\n");
@@ -163,11 +216,17 @@ void f2fs_parse_options(int argc, char *argv[])
c.fix_on = 1;
MSG(0, "Info: Force to fix corruption\n");
break;
+ case 'q':
+ c.preserve_limits = atoi(optarg);
+ MSG(0, "Info: Preserve quota limits = %d\n",
+ c.preserve_limits);
+ break;
+ case 'S':
+ c.sparse_mode = 1;
+ break;
case 't':
c.show_dentry = 1;
break;
-
-
case ':':
if (optopt == 'p') {
MSG(0, "Info: Use default preen mode\n");
@@ -189,7 +248,7 @@ void f2fs_parse_options(int argc, char *argv[])
break;
}
} else if (!strcmp("dump.f2fs", prog)) {
- const char *option_string = "d:i:n:s:a:b:";
+ const char *option_string = "d:i:n:s:Sa:b:";
static struct dump_option dump_opt = {
.nid = 0, /* default root ino */
.start_nat = -1,
@@ -233,6 +292,9 @@ void f2fs_parse_options(int argc, char *argv[])
&dump_opt.start_sit,
&dump_opt.end_sit);
break;
+ case 'S':
+ c.sparse_mode = 1;
+ break;
case 'a':
ret = sscanf(optarg, "%d~%d",
&dump_opt.start_ssa,
@@ -257,7 +319,7 @@ void f2fs_parse_options(int argc, char *argv[])
c.private = &dump_opt;
} else if (!strcmp("defrag.f2fs", prog)) {
- const char *option_string = "d:s:l:t:i";
+ const char *option_string = "d:s:Sl:t:i";
c.func = DEFRAG;
while ((option = getopt(argc, argv, option_string)) != EOF) {
@@ -281,6 +343,9 @@ void f2fs_parse_options(int argc, char *argv[])
ret = sscanf(optarg, "%"PRIx64"",
&c.defrag_start);
break;
+ case 'S':
+ c.sparse_mode = 1;
+ break;
case 'l':
if (strncmp(optarg, "0x", 2))
ret = sscanf(optarg, "%"PRIu64"",
@@ -342,11 +407,20 @@ void f2fs_parse_options(int argc, char *argv[])
break;
}
} else if (!strcmp("sload.f2fs", prog)) {
- const char *option_string = "d:f:t:";
+ const char *option_string = "C:d:f:p:s:St:T:";
+#ifdef HAVE_LIBSELINUX
+ int max_nr_opt = (int)sizeof(c.seopt_file) /
+ sizeof(c.seopt_file[0]);
+ char *token;
+#endif
+ char *p;
c.func = SLOAD;
while ((option = getopt(argc, argv, option_string)) != EOF) {
switch (option) {
+ case 'C':
+ c.fs_config_file = absolute_path(optarg);
+ break;
case 'd':
if (!is_digits(optarg)) {
err = EWRONG_OPT;
@@ -357,11 +431,40 @@ void f2fs_parse_options(int argc, char *argv[])
c.dbg_lv);
break;
case 'f':
- c.from_dir = (char *)optarg;
+ c.from_dir = absolute_path(optarg);
+ break;
+ case 'p':
+ c.target_out_dir = absolute_path(optarg);
+ break;
+ case 's':
+#ifdef HAVE_LIBSELINUX
+ token = strtok(optarg, ",");
+ while (token) {
+ if (c.nr_opt == max_nr_opt) {
+ MSG(0, "\tError: Expected at most %d selinux opts\n",
+ max_nr_opt);
+ error_out(prog);
+ }
+ c.seopt_file[c.nr_opt].type =
+ SELABEL_OPT_PATH;
+ c.seopt_file[c.nr_opt].value =
+ absolute_path(token);
+ c.nr_opt++;
+ token = strtok(NULL, ",");
+ }
+#else
+ MSG(0, "Info: Not support selinux opts\n");
+#endif
+ break;
+ case 'S':
+ c.sparse_mode = 1;
break;
case 't':
c.mount_point = (char *)optarg;
break;
+ case 'T':
+ c.fixed_time = strtoul(optarg, &p, 0);
+ break;
default:
err = EUNKNOWN_OPT;
break;
@@ -407,6 +510,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
u32 flag = le32_to_cpu(ckpt->ckpt_flags);
u32 blk_cnt;
+ errcode_t ret;
fsck_init(sbi);
@@ -429,7 +533,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
c.fix_on = 1;
break;
}
- } else {
+ } else if (c.preen_mode) {
/*
* we can hit this in 3 situations:
* 1. fsck -f, fix_on has already been set to 1 when
@@ -443,12 +547,23 @@ static void do_fsck(struct f2fs_sb_info *sbi)
c.fix_on = 1;
}
- fsck_chk_orphan_node(sbi);
+ fsck_chk_quota_node(sbi);
/* Traverse all block recursively from root inode */
blk_cnt = 1;
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
+ ret = quota_init_context(sbi);
+ if (ret) {
+ ASSERT_MSG("quota_init_context failure: %d", ret);
+ return;
+ }
+ }
+ fsck_chk_orphan_node(sbi);
fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
+ fsck_chk_quota_files(sbi);
+
fsck_verify(sbi);
fsck_free(sbi);
}
@@ -554,14 +669,13 @@ static int do_resize(struct f2fs_sb_info *sbi)
static int do_sload(struct f2fs_sb_info *sbi)
{
if (!c.from_dir) {
- MSG(0, "\tError: Need source directory\n");
- sload_usage();
- return -1;
+ MSG(0, "Info: No source directory, but it's okay.\n");
+ return 0;
}
if (!c.mount_point)
c.mount_point = "/";
- return f2fs_sload(sbi, c.from_dir, c.mount_point, NULL, NULL);
+ return f2fs_sload(sbi);
}
int main(int argc, char **argv)
@@ -590,6 +704,7 @@ int main(int argc, char **argv)
/* Get device */
if (f2fs_get_device_info() < 0)
return -1;
+
fsck_again:
memset(&gfsck, 0, sizeof(gfsck));
gfsck.sbi.fsck = &gfsck;
@@ -608,23 +723,39 @@ fsck_again:
case FSCK:
do_fsck(sbi);
break;
+#ifdef WITH_DUMP
case DUMP:
do_dump(sbi);
break;
-#ifndef WITH_ANDROID
+#endif
+#ifdef WITH_DEFRAG
case DEFRAG:
ret = do_defrag(sbi);
if (ret)
goto out_err;
break;
+#endif
+#ifdef WITH_RESIZE
case RESIZE:
if (do_resize(sbi))
goto out_err;
break;
+#endif
+#ifdef WITH_SLOAD
case SLOAD:
- do_sload(sbi);
- break;
+ if (do_sload(sbi))
+ goto out_err;
+
+ f2fs_do_umount(sbi);
+
+ /* fsck to fix missing quota */
+ c.func = FSCK;
+ c.fix_on = 1;
+ goto fsck_again;
#endif
+ default:
+ ERR_MSG("Wrong program name\n");
+ ASSERT(0);
}
f2fs_do_umount(sbi);
@@ -647,7 +778,9 @@ retry:
goto fsck_again;
}
}
- f2fs_finalize_device();
+ ret = f2fs_finalize_device();
+ if (ret < 0)
+ return ret;
printf("\nDone.\n");
return 0;
diff --git a/fsck/mkquota.c b/fsck/mkquota.c
new file mode 100644
index 0000000..b54be08
--- /dev/null
+++ b/fsck/mkquota.c
@@ -0,0 +1,404 @@
+/*
+ * mkquota.c --- create quota files for a filesystem
+ *
+ * Aditya Kali <adityakali@google.com>
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ */
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "quotaio.h"
+#include "quotaio_v2.h"
+#include "quotaio_tree.h"
+#include "common.h"
+#include "dict.h"
+
+
+/* Needed for architectures where sizeof(int) != sizeof(void *) */
+#define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
+#define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr))
+
+#if DEBUG_QUOTA
+static void print_dquot(const char *desc, struct dquot *dq)
+{
+ if (desc)
+ fprintf(stderr, "%s: ", desc);
+ fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
+ dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
+ (long long) dq->dq_dqb.dqb_bsoftlimit,
+ (long long) dq->dq_dqb.dqb_bhardlimit,
+ (long long) dq->dq_dqb.dqb_curinodes,
+ (long long) dq->dq_dqb.dqb_isoftlimit,
+ (long long) dq->dq_dqb.dqb_ihardlimit);
+}
+#else
+#define print_dquot(...)
+#endif
+
+static void write_dquots(dict_t *dict, struct quota_handle *qh)
+{
+ dnode_t *n;
+ struct dquot *dq;
+
+ for (n = dict_first(dict); n; n = dict_next(dict, n)) {
+ dq = dnode_get(n);
+ if (dq) {
+ print_dquot("write", dq);
+ dq->dq_h = qh;
+ update_grace_times(dq);
+ qh->qh_ops->commit_dquot(dq);
+ }
+ }
+}
+
+errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ quota_ctx_t qctx = fsck->qctx;
+ struct quota_handle *h = NULL;
+ int retval = 0;
+ dict_t *dict;
+
+ if ((!qctx) || (!sb->qf_ino[qtype]))
+ return 0;
+
+ retval = quota_get_mem(sizeof(struct quota_handle), &h);
+ if (retval) {
+ log_debug("Unable to allocate quota handle");
+ goto out;
+ }
+
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ retval = quota_file_create(sbi, h, qtype);
+ if (retval) {
+ log_debug("Cannot initialize io on quotafile");
+ } else {
+ write_dquots(dict, h);
+ quota_file_close(sbi, h, 1);
+ }
+ }
+out:
+ if (h)
+ quota_free_mem(&h);
+ return retval;
+}
+
+/******************************************************************/
+/* Helper functions for computing quota in memory. */
+/******************************************************************/
+
+static int dict_uint_cmp(const void *a, const void *b)
+{
+ unsigned int c, d;
+
+ c = VOIDPTR_TO_UINT(a);
+ d = VOIDPTR_TO_UINT(b);
+
+ if (c == d)
+ return 0;
+ else if (c > d)
+ return 1;
+ else
+ return -1;
+}
+
+static inline qid_t get_qid(struct f2fs_inode *inode, enum quota_type qtype)
+{
+ switch (qtype) {
+ case USRQUOTA:
+ return inode->i_uid;
+ case GRPQUOTA:
+ return inode->i_gid;
+ case PRJQUOTA:
+ return inode->i_projid;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static void quota_dnode_free(dnode_t *node, void *UNUSED(context))
+{
+ void *ptr = node ? dnode_get(node) : 0;
+
+ quota_free_mem(&ptr);
+ free(node);
+}
+
+/*
+ * Set up the quota tracking data structures.
+ */
+errcode_t quota_init_context(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ errcode_t err;
+ dict_t *dict;
+ quota_ctx_t ctx;
+ enum quota_type qtype;
+
+ err = quota_get_mem(sizeof(struct quota_ctx), &ctx);
+ if (err) {
+ log_debug("Failed to allocate quota context");
+ return err;
+ }
+
+ memset(ctx, 0, sizeof(struct quota_ctx));
+ dict_init(&ctx->linked_inode_dict, DICTCOUNT_T_MAX, dict_uint_cmp);
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ ctx->quota_file[qtype] = NULL;
+ if (!sb->qf_ino[qtype])
+ continue;
+ err = quota_get_mem(sizeof(dict_t), &dict);
+ if (err) {
+ log_debug("Failed to allocate dictionary");
+ quota_release_context(&ctx);
+ return err;
+ }
+ ctx->quota_dict[qtype] = dict;
+ dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
+ dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
+ }
+ ctx->sbi = sbi;
+ fsck->qctx = ctx;
+ return 0;
+}
+
+void quota_release_context(quota_ctx_t *qctx)
+{
+ dict_t *dict;
+ enum quota_type qtype;
+ quota_ctx_t ctx;
+
+ if (!qctx)
+ return;
+
+ ctx = *qctx;
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ dict = ctx->quota_dict[qtype];
+ ctx->quota_dict[qtype] = 0;
+ if (dict) {
+ dict_free_nodes(dict);
+ free(dict);
+ }
+ }
+ dict_free_nodes(&ctx->linked_inode_dict);
+ *qctx = NULL;
+ free(ctx);
+}
+
+static struct dquot *get_dq(dict_t *dict, __u32 key)
+{
+ struct dquot *dq;
+ dnode_t *n;
+
+ n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
+ if (n)
+ dq = dnode_get(n);
+ else {
+ if (quota_get_mem(sizeof(struct dquot), &dq)) {
+ log_err("Unable to allocate dquot");
+ return NULL;
+ }
+ memset(dq, 0, sizeof(struct dquot));
+ dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
+ dq->dq_id = key;
+ }
+ return dq;
+}
+
+/*
+ * Called to update the blocks used by a particular inode
+ */
+void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space)
+{
+ struct dquot *dq;
+ dict_t *dict;
+ enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ if (dq)
+ dq->dq_dqb.dqb_curspace += space;
+ }
+ }
+}
+
+/*
+ * Called to remove some blocks used by a particular inode
+ */
+void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space)
+{
+ struct dquot *dq;
+ dict_t *dict;
+ enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ dq->dq_dqb.dqb_curspace -= space;
+ }
+ }
+}
+
+/*
+ * Called to count the files used by an inode's user/group
+ */
+void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust)
+{
+ struct dquot *dq;
+ dict_t *dict; enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ dq->dq_dqb.dqb_curinodes += adjust;
+ }
+ }
+}
+
+/*
+ * Called from fsck to count quota.
+ */
+void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino,
+ struct f2fs_inode* inode)
+{
+ if (qctx) {
+ /* Handle hard linked inodes */
+ if (inode->i_links > 1) {
+ if (dict_lookup(&qctx->linked_inode_dict,
+ UINT_TO_VOIDPTR(ino))) {
+ return;
+ }
+ dict_alloc_insert(&qctx->linked_inode_dict,
+ UINT_TO_VOIDPTR(ino), NULL);
+ }
+
+ qsize_t space = (inode->i_blocks - 1) * BLOCK_SZ;
+ quota_data_add(qctx, inode, space);
+ quota_data_inodes(qctx, inode, +1);
+ }
+}
+
+struct scan_dquots_data {
+ dict_t *quota_dict;
+ int update_limits; /* update limits from disk */
+ int update_usage;
+ int usage_is_inconsistent;
+};
+
+static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
+{
+ struct scan_dquots_data *scan_data = cb_data;
+ dict_t *quota_dict = scan_data->quota_dict;
+ struct dquot *dq;
+
+ dq = get_dq(quota_dict, dquot->dq_id);
+ dq->dq_id = dquot->dq_id;
+ dq->dq_flags |= DQF_SEEN;
+
+ print_dquot("mem", dq);
+ print_dquot("dsk", dquot);
+ /* Check if there is inconsistency */
+ if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
+ dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
+ scan_data->usage_is_inconsistent = 1;
+ log_debug("[QUOTA WARNING] Usage inconsistent for ID %u:"
+ "actual (%lld, %lld) != expected (%lld, %lld)\n",
+ dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
+ (long long) dq->dq_dqb.dqb_curinodes,
+ (long long) dquot->dq_dqb.dqb_curspace,
+ (long long) dquot->dq_dqb.dqb_curinodes);
+ }
+
+ if (scan_data->update_limits) {
+ dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
+ dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
+ dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
+ dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
+ }
+
+ if (scan_data->update_usage) {
+ dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
+ dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
+ }
+
+ return 0;
+}
+
+/*
+ * Compares the measured quota in qctx->quota_dict with that in the quota inode
+ * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
+ * set to 1 if the supplied and on-disk quota usage values are not identical.
+ */
+errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi,
+ enum quota_type qtype, int *usage_inconsistent,
+ int preserve_limits)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ quota_ctx_t qctx = fsck->qctx;
+ struct quota_handle qh;
+ struct scan_dquots_data scan_data;
+ struct dquot *dq;
+ dnode_t *n;
+ dict_t *dict = qctx->quota_dict[qtype];
+ errcode_t err = 0;
+
+ if (!dict)
+ goto out;
+
+ err = quota_file_open(sbi, &qh, qtype, 0);
+ if (err) {
+ log_debug("Open quota file failed");
+ goto out;
+ }
+
+ scan_data.quota_dict = qctx->quota_dict[qtype];
+ scan_data.update_limits = preserve_limits;
+ scan_data.update_usage = 0;
+ scan_data.usage_is_inconsistent = 0;
+ err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
+ if (err) {
+ log_debug("Error scanning dquots");
+ goto out;
+ }
+
+ for (n = dict_first(dict); n; n = dict_next(dict, n)) {
+ dq = dnode_get(n);
+ if (!dq)
+ continue;
+ if ((dq->dq_flags & DQF_SEEN) == 0) {
+ log_debug("[QUOTA WARNING] "
+ "Missing quota entry ID %d\n", dq->dq_id);
+ scan_data.usage_is_inconsistent = 1;
+ }
+ }
+ *usage_inconsistent = scan_data.usage_is_inconsistent;
+
+out:
+ return err;
+}
+
diff --git a/fsck/mount.c b/fsck/mount.c
index a0b0bea..678eeae 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -9,7 +9,24 @@
* published by the Free Software Foundation.
*/
#include "fsck.h"
+#include "xattr.h"
#include <locale.h>
+#ifdef HAVE_LINUX_POSIX_ACL_H
+#include <linux/posix_acl.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
+#ifndef ACL_UNDEFINED_TAG
+#define ACL_UNDEFINED_TAG (0x00)
+#define ACL_USER_OBJ (0x01)
+#define ACL_USER (0x02)
+#define ACL_GROUP_OBJ (0x04)
+#define ACL_GROUP (0x08)
+#define ACL_MASK (0x10)
+#define ACL_OTHER (0x20)
+#endif
u32 get_free_segments(struct f2fs_sb_info *sbi)
{
@@ -30,17 +47,138 @@ void update_free_segments(struct f2fs_sb_info *sbi)
char *progress = "-*|*-";
static int i = 0;
+ if (c.dbg_lv)
+ return;
+
MSG(0, "\r [ %c ] Free segments: 0x%x", progress[i % 5], get_free_segments(sbi));
fflush(stdout);
i++;
}
-void print_inode_info(struct f2fs_inode *inode, int name)
+#if defined(HAVE_LINUX_POSIX_ACL_H) || defined(HAVE_SYS_ACL_H)
+void print_acl(char *value, int size)
{
+ struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
+ struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
+ const char *end = value + size;
+ int i, count;
+
+ if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) {
+ MSG(0, "Invalid ACL version [0x%x : 0x%x]\n",
+ le32_to_cpu(hdr->a_version), F2FS_ACL_VERSION);
+ return;
+ }
+
+ count = f2fs_acl_count(size);
+ if (count <= 0) {
+ MSG(0, "Invalid ACL value size %d\n", size);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ if ((char *)entry > end) {
+ MSG(0, "Invalid ACL entries count %d\n", count);
+ return;
+ }
+
+ switch (le16_to_cpu(entry->e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ MSG(0, "tag:0x%x perm:0x%x\n",
+ le16_to_cpu(entry->e_tag),
+ le16_to_cpu(entry->e_perm));
+ entry = (struct f2fs_acl_entry *)((char *)entry +
+ sizeof(struct f2fs_acl_entry_short));
+ break;
+ case ACL_USER:
+ MSG(0, "tag:0x%x perm:0x%x uid:%u\n",
+ le16_to_cpu(entry->e_tag),
+ le16_to_cpu(entry->e_perm),
+ le32_to_cpu(entry->e_id));
+ entry = (struct f2fs_acl_entry *)((char *)entry +
+ sizeof(struct f2fs_acl_entry));
+ break;
+ case ACL_GROUP:
+ MSG(0, "tag:0x%x perm:0x%x gid:%u\n",
+ le16_to_cpu(entry->e_tag),
+ le16_to_cpu(entry->e_perm),
+ le32_to_cpu(entry->e_id));
+ entry = (struct f2fs_acl_entry *)((char *)entry +
+ sizeof(struct f2fs_acl_entry));
+ break;
+ default:
+ MSG(0, "Unknown ACL tag 0x%x\n",
+ le16_to_cpu(entry->e_tag));
+ return;
+ }
+ }
+}
+#else
+#define print_acl(value, size) do { \
+ int i; \
+ for (i = 0; i < size; i++) \
+ MSG(0, "%02X", value[i]); \
+ MSG(0, "\n"); \
+} while (0)
+#endif
+
+void print_xattr_entry(struct f2fs_xattr_entry *ent)
+{
+ char *value = (char *)(ent->e_name + le16_to_cpu(ent->e_name_len));
+ struct fscrypt_context *ctx;
+ int i;
+
+ MSG(0, "\nxattr: e_name_index:%d e_name:", ent->e_name_index);
+ for (i = 0; i < le16_to_cpu(ent->e_name_len); i++)
+ MSG(0, "%c", ent->e_name[i]);
+ MSG(0, " e_name_len:%d e_value_size:%d e_value:\n",
+ ent->e_name_len, le16_to_cpu(ent->e_value_size));
+
+ switch (ent->e_name_index) {
+ case F2FS_XATTR_INDEX_POSIX_ACL_ACCESS:
+ case F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT:
+ print_acl(value, le16_to_cpu(ent->e_value_size));
+ break;
+ case F2FS_XATTR_INDEX_USER:
+ case F2FS_XATTR_INDEX_SECURITY:
+ case F2FS_XATTR_INDEX_TRUSTED:
+ case F2FS_XATTR_INDEX_LUSTRE:
+ for (i = 0; i < le16_to_cpu(ent->e_value_size); i++)
+ MSG(0, "%02X", value[i]);
+ MSG(0, "\n");
+ break;
+ case F2FS_XATTR_INDEX_ENCRYPTION:
+ ctx = (struct fscrypt_context *)value;
+ MSG(0, "format: %d\n", ctx->format);
+ MSG(0, "contents_encryption_mode: 0x%x\n", ctx->contents_encryption_mode);
+ MSG(0, "filenames_encryption_mode: 0x%x\n", ctx->filenames_encryption_mode);
+ MSG(0, "flags: 0x%x\n", ctx->flags);
+ MSG(0, "master_key_descriptor: ");
+ for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++)
+ MSG(0, "%02X", ctx->master_key_descriptor[i]);
+ MSG(0, "\nnonce: ");
+ for (i = 0; i < FS_KEY_DERIVATION_NONCE_SIZE; i++)
+ MSG(0, "%02X", ctx->nonce[i]);
+ MSG(0, "\n");
+ break;
+ default:
+ break;
+ }
+}
+
+void print_inode_info(struct f2fs_sb_info *sbi,
+ struct f2fs_node *node, int name)
+{
+ struct f2fs_inode *inode = &node->i;
+ void *xattr_addr;
+ struct f2fs_xattr_entry *ent;
unsigned char en[F2FS_NAME_LEN + 1];
unsigned int i = 0;
int namelen = le32_to_cpu(inode->i_namelen);
int enc_name = file_enc_name(inode);
+ int ofs = __get_extra_isize(inode);
namelen = convert_encrypted_name(inode->i_name, namelen, en, enc_name);
en[namelen] = '\0';
@@ -87,17 +225,26 @@ void print_inode_info(struct f2fs_inode *inode, int name)
le32_to_cpu(inode->i_ext.blk_addr),
le32_to_cpu(inode->i_ext.len));
- DISP_u32(inode, i_addr[0]); /* Pointers to data blocks */
- DISP_u32(inode, i_addr[1]); /* Pointers to data blocks */
- DISP_u32(inode, i_addr[2]); /* Pointers to data blocks */
- DISP_u32(inode, i_addr[3]); /* Pointers to data blocks */
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ DISP_u16(inode, i_extra_isize);
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR))
+ DISP_u16(inode, i_inline_xattr_size);
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
+ DISP_u32(inode, i_projid);
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ DISP_u32(inode, i_inode_checksum);
+ }
+
+ DISP_u32(inode, i_addr[ofs]); /* Pointers to data blocks */
+ DISP_u32(inode, i_addr[ofs + 1]); /* Pointers to data blocks */
+ DISP_u32(inode, i_addr[ofs + 2]); /* Pointers to data blocks */
+ DISP_u32(inode, i_addr[ofs + 3]); /* Pointers to data blocks */
- for (i = 4; i < ADDRS_PER_INODE(inode); i++) {
- if (inode->i_addr[i] != 0x0) {
- printf("i_addr[0x%x] points data block\r\t\t[0x%4x]\n",
- i, le32_to_cpu(inode->i_addr[i]));
+ for (i = ofs + 3; i < ADDRS_PER_INODE(inode); i++) {
+ if (inode->i_addr[i] == 0x0)
break;
- }
+ printf("i_addr[0x%x] points data block\t\t[0x%4x]\n",
+ i, le32_to_cpu(inode->i_addr[i]));
}
DISP_u32(inode, i_nid[0]); /* direct */
@@ -106,17 +253,24 @@ void print_inode_info(struct f2fs_inode *inode, int name)
DISP_u32(inode, i_nid[3]); /* indirect */
DISP_u32(inode, i_nid[4]); /* double indirect */
+ xattr_addr = read_all_xattrs(sbi, node);
+ list_for_each_xattr(ent, xattr_addr) {
+ print_xattr_entry(ent);
+ }
+ free(xattr_addr);
+
printf("\n");
}
-void print_node_info(struct f2fs_node *node_block, int verbose)
+void print_node_info(struct f2fs_sb_info *sbi,
+ struct f2fs_node *node_block, int verbose)
{
nid_t ino = le32_to_cpu(node_block->footer.ino);
nid_t nid = le32_to_cpu(node_block->footer.nid);
/* Is this inode? */
if (ino == nid) {
DBG(verbose, "Node ID [0x%x:%u] is inode\n", nid, nid);
- print_inode_info(&node_block->i, verbose);
+ print_inode_info(sbi, node_block, verbose);
} else {
int i;
u32 *dump_blk = (u32 *)node_block;
@@ -278,7 +432,22 @@ void print_sb_state(struct f2fs_super_block *sb)
MSG(0, "%s", " encrypt");
}
if (f & cpu_to_le32(F2FS_FEATURE_BLKZONED)) {
- MSG(0, "%s", " zoned block device");
+ MSG(0, "%s", " blkzoned");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ MSG(0, "%s", " extra_attr");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) {
+ MSG(0, "%s", " project_quota");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) {
+ MSG(0, "%s", " inode_checksum");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) {
+ MSG(0, "%s", " flexible_inline_xattr");
+ }
+ if (f & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
+ MSG(0, "%s", " quota_ino");
}
MSG(0, "\n");
MSG(0, "Info: superblock encrypt level = %d, salt = ",
@@ -421,6 +590,7 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, u64 offset)
int validate_super_block(struct f2fs_sb_info *sbi, int block)
{
u64 offset;
+ char buf[F2FS_BLKSIZE];
sbi->raw_super = malloc(sizeof(struct f2fs_super_block));
@@ -429,9 +599,12 @@ int validate_super_block(struct f2fs_sb_info *sbi, int block)
else
offset = F2FS_BLKSIZE + F2FS_SUPER_OFFSET;
- if (dev_read(sbi->raw_super, offset, sizeof(struct f2fs_super_block)))
+ if (dev_read_block(buf, block))
return -1;
+ memcpy(sbi->raw_super, buf + F2FS_SUPER_OFFSET,
+ sizeof(struct f2fs_super_block));
+
if (!sanity_check_raw_super(sbi->raw_super, offset)) {
/* get kernel version */
if (c.kd >= 0) {
@@ -722,7 +895,7 @@ static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi)
nid_t nid;
int i;
- if (!(c.func == SLOAD))
+ if (!(c.func == SLOAD || c.func == FSCK))
return 0;
nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1);
@@ -1357,8 +1530,10 @@ void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid,
/* check its block address */
if (node_blk->footer.nid == node_blk->footer.ino) {
- oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs_in_node]);
- node_blk->i.i_addr[ofs_in_node] = cpu_to_le32(newaddr);
+ int ofs = get_extra_isize(node_blk);
+
+ oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs + ofs_in_node]);
+ node_blk->i.i_addr[ofs + ofs_in_node] = cpu_to_le32(newaddr);
} else {
oldaddr = le32_to_cpu(node_blk->dn.addr[ofs_in_node]);
node_blk->dn.addr[ofs_in_node] = cpu_to_le32(newaddr);
@@ -1845,7 +2020,7 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
if (le32_to_cpu(nid_in_journal(journal, i)) == nid) {
memset(&nat_in_journal(journal, i), 0,
sizeof(struct f2fs_nat_entry));
- FIX_MSG("Remove nid [0x%x] in nat journal\n", nid);
+ FIX_MSG("Remove nid [0x%x] in nat journal", nid);
return;
}
}
@@ -1858,8 +2033,15 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
ret = dev_read_block(nat_block, block_addr);
ASSERT(ret >= 0);
- memset(&nat_block->entries[entry_off], 0,
+ if (nid == F2FS_NODE_INO(sbi) || nid == F2FS_META_INO(sbi)) {
+ FIX_MSG("nid [0x%x] block_addr= 0x%x -> 0x1", nid,
+ le32_to_cpu(nat_block->entries[entry_off].block_addr));
+ nat_block->entries[entry_off].block_addr = cpu_to_le32(0x1);
+ } else {
+ memset(&nat_block->entries[entry_off], 0,
sizeof(struct f2fs_nat_entry));
+ FIX_MSG("Remove nid [0x%x] in NAT", nid);
+ }
ret = dev_write_block(nat_block, block_addr);
ASSERT(ret >= 0);
@@ -1918,13 +2100,17 @@ void write_checkpoint(struct f2fs_sb_info *sbi)
ASSERT(ret >= 0);
}
- /* write the last cp */
- ret = dev_write_block(cp, cp_blk_no++);
- ASSERT(ret >= 0);
-
/* Write nat bits */
if (flags & CP_NAT_BITS_FLAG)
write_nat_bits(sbi, sb, cp, sbi->cur_cp);
+
+ /* in case of sudden power off */
+ ret = f2fs_fsync_device();
+ ASSERT(ret >= 0);
+
+ /* write the last cp */
+ ret = dev_write_block(cp, cp_blk_no++);
+ ASSERT(ret >= 0);
}
void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
@@ -1978,13 +2164,14 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
if ((nid + i) == F2FS_NODE_INO(sbi) ||
(nid + i) == F2FS_META_INO(sbi)) {
- /* block_addr of node/meta inode should be 0x1 */
+ /*
+ * block_addr of node/meta inode should be 0x1.
+ * Set this bit, and fsck_verify will fix it.
+ */
if (le32_to_cpu(nat_block->entries[i].block_addr) != 0x1) {
- FIX_MSG("ino: 0x%x node/meta inode, block_addr= 0x%x -> 0x1",
+ ASSERT_MSG("\tError: ino[0x%x] block_addr[0x%x] is invalid\n",
nid + i, le32_to_cpu(nat_block->entries[i].block_addr));
- nat_block->entries[i].block_addr = cpu_to_le32(0x1);
- ret = dev_write_block(nat_block, block_addr);
- ASSERT(ret >= 0);
+ f2fs_set_bit(nid + i, fsck->nat_area_bitmap);
}
continue;
}
@@ -2009,7 +2196,6 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
*/
ASSERT_MSG("Invalid nat entry[0]: "
"blk_addr[0x%x]\n", ni.blk_addr);
- c.fix_on = 1;
fsck->chk.valid_nat_entry_cnt--;
}
@@ -2137,13 +2323,22 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
if (c.auto_fix || c.preen_mode) {
u32 flag = get_cp(ckpt_flags);
- if (flag & CP_FSCK_FLAG)
+ if (flag & CP_FSCK_FLAG ||
+ (exist_qf_ino(sb) && (!(flag & CP_UMOUNT_FLAG) ||
+ flag & CP_ERROR_FLAG))) {
c.fix_on = 1;
- else if (!c.preen_mode)
+ } else if (!c.preen_mode) {
+ print_cp_state(flag);
return 1;
+ }
}
c.bug_on = 0;
+ c.feature = sb->feature;
+
+ /* precompute checksum seed for metadata */
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ c.chksum_seed = f2fs_cal_crc32(~0, sb->uuid, sizeof(sb->uuid));
sbi->total_valid_node_count = get_cp(valid_node_count);
sbi->total_valid_inode_count = get_cp(valid_inode_count);
@@ -2197,7 +2392,7 @@ void f2fs_do_umount(struct f2fs_sb_info *sbi)
unsigned int i;
/* free nm_info */
- if (c.func == SLOAD)
+ if (c.func == SLOAD || c.func == FSCK)
free(nm_i->nid_bitmap);
free(nm_i->nat_bitmap);
free(sbi->nm_info);
diff --git a/fsck/node.c b/fsck/node.c
index fe923e5..7f4a28b 100644
--- a/fsck/node.c
+++ b/fsck/node.c
@@ -63,7 +63,7 @@ block_t new_node_block(struct f2fs_sb_info *sbi,
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
struct f2fs_summary sum;
struct node_info ni;
- block_t blkaddr;
+ block_t blkaddr = NULL_ADDR;
int type;
f2fs_inode = dn->inode_blk;
@@ -105,10 +105,10 @@ block_t new_node_block(struct f2fs_sb_info *sbi,
*
* By default, it sets inline_xattr and inline_data
*/
-static int get_node_path(unsigned long block,
+static int get_node_path(struct f2fs_node *node, long block,
int offset[4], unsigned int noffset[4])
{
- const long direct_index = DEF_ADDRS_PER_INODE_INLINE_XATTR;
+ const long direct_index = ADDRS_PER_INODE(&node->i);
const long direct_blks = ADDRS_PER_BLOCK;
const long dptrs_per_blk = NIDS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
@@ -191,7 +191,7 @@ void get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
int level, i;
int ret;
- level = get_node_path(index, offset, noffset);
+ level = get_node_path(dn->inode_blk, index, offset, noffset);
nids[0] = dn->nid;
parent = dn->inode_blk;
diff --git a/fsck/node.h b/fsck/node.h
index 721e5b7..cbf7ed7 100644
--- a/fsck/node.h
+++ b/fsck/node.h
@@ -26,9 +26,14 @@ static inline int IS_INODE(struct f2fs_node *node)
return ((node)->footer.nid == (node)->footer.ino);
}
+static inline __le32 *blkaddr_in_inode(struct f2fs_node *node)
+{
+ return node->i.i_addr + get_extra_isize(node);
+}
+
static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
{
- return IS_INODE(node) ? node->i.i_addr : node->dn.addr;
+ return IS_INODE(node) ? blkaddr_in_inode(node) : node->dn.addr;
}
static inline block_t datablock_addr(struct f2fs_node *node_page,
diff --git a/fsck/quotaio.c b/fsck/quotaio.c
new file mode 100644
index 0000000..afadf56
--- /dev/null
+++ b/fsck/quotaio.c
@@ -0,0 +1,221 @@
+/** quotaio.c
+ *
+ * Generic IO operations on quotafiles
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ * Aditya Kali <adityakali@google.com> - Ported to e2fsprogs
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <assert.h>
+
+#include "common.h"
+#include "quotaio.h"
+
+static const char * const extensions[MAXQUOTAS] = {
+ [USRQUOTA] = "user",
+ [GRPQUOTA] = "group",
+ [PRJQUOTA] = "project",
+};
+
+/* Header in all newer quotafiles */
+struct disk_dqheader {
+ __le32 dqh_magic;
+ __le32 dqh_version;
+} __attribute__ ((packed));
+
+/**
+ * Convert type of quota to written representation
+ */
+const char *quota_type2name(enum quota_type qtype)
+{
+ if (qtype >= MAXQUOTAS)
+ return "unknown";
+ return extensions[qtype];
+}
+
+/*
+ * Set grace time if needed
+ */
+void update_grace_times(struct dquot *q)
+{
+ time_t now;
+
+ time(&now);
+ if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) >
+ q->dq_dqb.dqb_bsoftlimit) {
+ if (!q->dq_dqb.dqb_btime)
+ q->dq_dqb.dqb_btime =
+ now + q->dq_h->qh_info.dqi_bgrace;
+ } else {
+ q->dq_dqb.dqb_btime = 0;
+ }
+
+ if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes >
+ q->dq_dqb.dqb_isoftlimit) {
+ if (!q->dq_dqb.dqb_itime)
+ q->dq_dqb.dqb_itime =
+ now + q->dq_h->qh_info.dqi_igrace;
+ } else {
+ q->dq_dqb.dqb_itime = 0;
+ }
+}
+
+/* Functions to read/write quota file. */
+static unsigned int quota_write_nomount(struct quota_file *qf,
+ long offset,
+ void *buf, unsigned int size)
+{
+ unsigned int written;
+
+ written = f2fs_write(qf->sbi, qf->ino, buf, size, offset);
+ if (qf->filesize < offset + written)
+ qf->filesize = offset + written;
+
+ return written;
+}
+
+static unsigned int quota_read_nomount(struct quota_file *qf, long offset,
+ void *buf, unsigned int size)
+{
+ return f2fs_read(qf->sbi, qf->ino, buf, size, offset);
+}
+
+/*
+ * Detect quota format and initialize quota IO
+ */
+errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ enum quota_type qtype, int flags)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ quota_ctx_t qctx = fsck->qctx;
+ f2fs_ino_t qf_ino;
+ errcode_t err = 0;
+ int allocated_handle = 0;
+
+ if (qtype >= MAXQUOTAS)
+ return EINVAL;
+
+ qf_ino = sb->qf_ino[qtype];
+
+ if (!h) {
+ if (qctx->quota_file[qtype]) {
+ h = qctx->quota_file[qtype];
+ (void) quota_file_close(sbi, h, 0);
+ }
+ err = quota_get_mem(sizeof(struct quota_handle), &h);
+ if (err) {
+ log_err("Unable to allocate quota handle");
+ return err;
+ }
+ allocated_handle = 1;
+ }
+
+ h->qh_qf.sbi = sbi;
+ h->qh_qf.ino = qf_ino;
+ h->write = quota_write_nomount;
+ h->read = quota_read_nomount;
+ h->qh_file_flags = flags;
+ h->qh_io_flags = 0;
+ h->qh_type = qtype;
+ h->qh_fmt = QFMT_VFS_V1;
+ memset(&h->qh_info, 0, sizeof(h->qh_info));
+ h->qh_ops = &quotafile_ops_2;
+
+ if (h->qh_ops->check_file &&
+ (h->qh_ops->check_file(h, qtype) == 0)) {
+ log_err("qh_ops->check_file failed");
+ err = EIO;
+ goto errout;
+ }
+
+ if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
+ log_err("qh_ops->init_io failed");
+ err = EIO;
+ goto errout;
+ }
+ if (allocated_handle)
+ qctx->quota_file[qtype] = h;
+errout:
+ return err;
+}
+
+/*
+ * Create new quotafile of specified format on given filesystem
+ */
+errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ enum quota_type qtype)
+{
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ f2fs_ino_t qf_inum = sb->qf_ino[qtype];
+ errcode_t err = 0;
+
+ h->qh_qf.sbi = sbi;
+ h->qh_qf.ino = qf_inum;
+ h->write = quota_write_nomount;
+ h->read = quota_read_nomount;
+
+ log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype);
+ h->qh_io_flags = 0;
+ h->qh_type = qtype;
+ h->qh_fmt = QFMT_VFS_V1;
+ memset(&h->qh_info, 0, sizeof(h->qh_info));
+ h->qh_ops = &quotafile_ops_2;
+
+ if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
+ log_err("qh_ops->new_io failed");
+ err = EIO;
+ }
+
+ return err;
+}
+
+/*
+ * Close quotafile and release handle
+ */
+errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ int update_filesize)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ quota_ctx_t qctx = fsck->qctx;
+
+ if (h->qh_io_flags & IOFL_INFODIRTY) {
+ if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
+ return EIO;
+ h->qh_io_flags &= ~IOFL_INFODIRTY;
+ }
+ if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
+ return EIO;
+ if (update_filesize) {
+ f2fs_filesize_update(sbi, h->qh_qf.ino, h->qh_qf.filesize);
+ }
+ if (qctx->quota_file[h->qh_type] == h)
+ quota_free_mem(&qctx->quota_file[h->qh_type]);
+ return 0;
+}
+
+/*
+ * Create empty quota structure
+ */
+struct dquot *get_empty_dquot(void)
+{
+ struct dquot *dquot;
+
+ if (quota_get_memzero(sizeof(struct dquot), &dquot)) {
+ log_err("Failed to allocate dquot");
+ return NULL;
+ }
+
+ dquot->dq_id = -1;
+ return dquot;
+}
diff --git a/fsck/quotaio.h b/fsck/quotaio.h
new file mode 100644
index 0000000..8087309
--- /dev/null
+++ b/fsck/quotaio.h
@@ -0,0 +1,256 @@
+/** quotaio.h
+ *
+ * Interface to the quota library.
+ *
+ * The quota library provides interface for creating and updating the quota
+ * files and the ext4 superblock fields. It supports the new VFS_V1 quota
+ * format. The quota library also provides support for keeping track of quotas
+ * in memory.
+ *
+ * Aditya Kali <adityakali@google.com>
+ * Header of IO operations for quota utilities
+ *
+ * Jan Kara <jack@suse.cz>
+ *
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ */
+
+#ifndef GUARD_QUOTAIO_H
+#define GUARD_QUOTAIO_H
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include "dict.h"
+#include "f2fs_fs.h"
+#include "f2fs.h"
+#include "node.h"
+#include "fsck.h"
+
+#include "dqblk_v2.h"
+
+typedef int64_t qsize_t; /* Type in which we store size limitations */
+typedef int32_t f2fs_ino_t;
+typedef int errcode_t;
+
+enum quota_type {
+ USRQUOTA = 0,
+ GRPQUOTA = 1,
+ PRJQUOTA = 2,
+ MAXQUOTAS = 3,
+};
+
+#if MAXQUOTAS > 32
+#error "cannot have more than 32 quota types to fit in qtype_bits"
+#endif
+
+
+#define QUOTA_USR_BIT (1 << USRQUOTA)
+#define QUOTA_GRP_BIT (1 << GRPQUOTA)
+#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
+#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
+
+typedef struct quota_ctx *quota_ctx_t;
+
+struct quota_ctx {
+ struct f2fs_sb_info *sbi;
+ struct dict_t *quota_dict[MAXQUOTAS];
+ struct quota_handle *quota_file[MAXQUOTAS];
+ struct dict_t linked_inode_dict;
+};
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927, /* GRPQUOTA */\
+ 0xd9c03f14 /* PRJQUOTA */\
+}
+
+/* Size of blocks in which are counted size limits in generic utility parts */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
+/* Quota format type IDs */
+#define QFMT_VFS_OLD 1
+#define QFMT_VFS_V0 2
+#define QFMT_VFS_V1 4
+
+/*
+ * The following constants define the default amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+
+#define IOFL_INFODIRTY 0x01 /* Did info change? */
+
+struct quotafile_ops;
+
+/* Generic information about quotafile */
+struct util_dqinfo {
+ time_t dqi_bgrace; /* Block grace time for given quotafile */
+ time_t dqi_igrace; /* Inode grace time for given quotafile */
+ union {
+ struct v2_mem_dqinfo v2_mdqi;
+ } u; /* Format specific info about quotafile */
+};
+
+struct quota_file {
+ struct f2fs_sb_info *sbi;
+ f2fs_ino_t ino;
+ int64_t filesize;
+};
+
+/* Structure for one opened quota file */
+struct quota_handle {
+ enum quota_type qh_type; /* Type of quotafile */
+ int qh_fmt; /* Quotafile format */
+ int qh_file_flags;
+ int qh_io_flags; /* IO flags for file */
+ struct quota_file qh_qf;
+ unsigned int (*read)(struct quota_file *qf, long offset,
+ void *buf, unsigned int size);
+ unsigned int (*write)(struct quota_file *qf, long offset,
+ void *buf, unsigned int size);
+ struct quotafile_ops *qh_ops; /* Operations on quotafile */
+ struct util_dqinfo qh_info; /* Generic quotafile info */
+};
+
+/* Utility quota block */
+struct util_dqblk {
+ qsize_t dqb_ihardlimit;
+ qsize_t dqb_isoftlimit;
+ qsize_t dqb_curinodes;
+ qsize_t dqb_bhardlimit;
+ qsize_t dqb_bsoftlimit;
+ qsize_t dqb_curspace;
+ time_t dqb_btime;
+ time_t dqb_itime;
+ union {
+ struct v2_mem_dqblk v2_mdqb;
+ } u; /* Format specific dquot information */
+};
+
+/* Structure for one loaded quota */
+struct dquot {
+ struct dquot *dq_next; /* Pointer to next dquot in the list */
+ qid_t dq_id; /* ID dquot belongs to */
+ int dq_flags; /* Some flags for utils */
+ struct quota_handle *dq_h; /* Handle of quotafile for this dquot */
+ struct util_dqblk dq_dqb; /* Parsed data of dquot */
+};
+
+#define DQF_SEEN 0x0001
+
+/* Structure of quotafile operations */
+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);
+ /* Create new quotafile */
+ int (*new_io) (struct quota_handle *h);
+ /* Write all changes and close quotafile */
+ int (*end_io) (struct quota_handle *h);
+ /* Write info about quotafile */
+ int (*write_info) (struct quota_handle *h);
+ /* Read dquot into memory */
+ struct dquot *(*read_dquot) (struct quota_handle *h, qid_t id);
+ /* Write given dquot to disk */
+ int (*commit_dquot) (struct dquot *dquot);
+ /* Scan quotafile and call callback on every structure */
+ int (*scan_dquots) (struct quota_handle *h,
+ int (*process_dquot) (struct dquot *dquot,
+ void *data),
+ void *data);
+ /* Function to print format specific file information */
+ int (*report) (struct quota_handle *h, int verbose);
+};
+
+#ifdef __CHECKER__
+# ifndef __bitwise
+# define __bitwise __attribute__((bitwise))
+# endif
+#define __force __attribute__((force))
+#else
+# ifndef __bitwise
+# define __bitwise
+# endif
+#define __force
+#endif
+
+#define be32_to_cpu(n) ntohl(n)
+
+/* Open existing quotafile of given type (and verify its format) on given
+ * filesystem. */
+errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ enum quota_type qtype, int flags);
+
+/* Create new quotafile of specified format on given filesystem */
+errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ enum quota_type qtype);
+
+/* Close quotafile */
+errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h,
+ int update_filesize);
+
+/* Get empty quota structure */
+struct dquot *get_empty_dquot(void);
+const char *quota_type2name(enum quota_type qtype);
+void update_grace_times(struct dquot *q);
+
+/* In mkquota.c */
+errcode_t quota_init_context(struct f2fs_sb_info *sbi);
+void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust);
+void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space);
+void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space);
+errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype);
+void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino,
+ struct f2fs_inode* inode);
+void quota_release_context(quota_ctx_t *qctx);
+errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi,
+ enum quota_type qtype, int *usage_inconsistent,
+ int preserve_limits);
+
+static inline errcode_t quota_get_mem(unsigned long size, void *ptr)
+{
+ void *pp;
+
+ pp = malloc(size);
+ if (!pp)
+ return -1;
+ memcpy(ptr, &pp, sizeof (pp));
+ return 0;
+}
+
+static inline errcode_t quota_get_memzero(unsigned long size, void *ptr)
+{
+ void *pp;
+
+ pp = malloc(size);
+ if (!pp)
+ return -1;
+ memset(pp, 0, size);
+ memcpy(ptr, &pp, sizeof(pp));
+ return 0;
+}
+
+static inline errcode_t quota_free_mem(void *ptr)
+{
+ void *p;
+
+ memcpy(&p, ptr, sizeof(p));
+ free(p);
+ p = 0;
+ memcpy(ptr, &p, sizeof(p));
+ return 0;
+}
+
+#endif /* GUARD_QUOTAIO_H */
diff --git a/fsck/quotaio_tree.c b/fsck/quotaio_tree.c
new file mode 100644
index 0000000..5aef228
--- /dev/null
+++ b/fsck/quotaio_tree.c
@@ -0,0 +1,679 @@
+/*
+ * Implementation of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "quotaio_tree.h"
+#include "quotaio.h"
+
+typedef char *dqbuf_t;
+
+#define freedqbuf(buf) quota_free_mem(&buf)
+
+static inline dqbuf_t getdqbuf(void)
+{
+ dqbuf_t buf;
+ if (quota_get_memzero(QT_BLKSIZE, &buf)) {
+ log_err("Failed to allocate dqbuf");
+ return NULL;
+ }
+
+ return buf;
+}
+
+/* Is given dquot empty? */
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
+{
+ unsigned int i;
+
+ for (i = 0; i < info->dqi_entry_size; i++)
+ if (disk[i])
+ return 0;
+ return 1;
+}
+
+int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
+{
+ return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
+ info->dqi_entry_size;
+}
+
+static int get_index(qid_t id, int depth)
+{
+ return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
+}
+
+static inline void mark_quotafile_info_dirty(struct quota_handle *h)
+{
+ h->qh_io_flags |= IOFL_INFODIRTY;
+}
+
+/* Read given block */
+static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
+{
+ int err;
+
+ err = h->read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
+ QT_BLKSIZE);
+ if (err < 0)
+ log_err("Cannot read block %u: %s", blk, strerror(errno));
+ else if (err != QT_BLKSIZE)
+ memset(buf + err, 0, QT_BLKSIZE - err);
+}
+
+/* Write block */
+static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
+{
+ int err;
+
+ err = h->write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
+ QT_BLKSIZE);
+ if (err < 0 && errno != ENOSPC)
+ log_err("Cannot write block (%u): %s", blk, strerror(errno));
+ if (err != QT_BLKSIZE)
+ return -ENOSPC;
+ return 0;
+}
+
+/* Get free block in file (either from free list or create new one) */
+static int get_free_dqblk(struct quota_handle *h)
+{
+ dqbuf_t buf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ int blk;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (info->dqi_free_blk) {
+ blk = info->dqi_free_blk;
+ read_blk(h, blk, buf);
+ info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+ } else {
+ memset(buf, 0, QT_BLKSIZE);
+ /* Assure block allocation... */
+ if (write_blk(h, info->dqi_blocks, buf) < 0) {
+ freedqbuf(buf);
+ log_err("Cannot allocate new quota block "
+ "(out of disk space).");
+ return -ENOSPC;
+ }
+ blk = info->dqi_blocks++;
+ }
+ mark_quotafile_info_dirty(h);
+ freedqbuf(buf);
+ return blk;
+}
+
+/* Put given block to free list */
+static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ dh->dqdh_entries = cpu_to_le16(0);
+ info->dqi_free_blk = blk;
+ mark_quotafile_info_dirty(h);
+ write_blk(h, blk, buf);
+}
+
+/* Remove given block from the list of blocks with free entries */
+static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ unsigned int nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk =
+ le32_to_cpu(dh->dqdh_prev_free);
+
+ if (!tmpbuf)
+ return;
+
+ if (nextblk) {
+ read_blk(h, nextblk, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+ dh->dqdh_prev_free;
+ write_blk(h, nextblk, tmpbuf);
+ }
+ if (prevblk) {
+ read_blk(h, prevblk, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
+ dh->dqdh_next_free;
+ write_blk(h, prevblk, tmpbuf);
+ } else {
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
+ mark_quotafile_info_dirty(h);
+ }
+ freedqbuf(tmpbuf);
+ dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+ write_blk(h, blk, buf); /* No matter whether write succeeds
+ * block is out of list */
+}
+
+/* Insert given block to the beginning of list with free entries */
+static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ if (!tmpbuf)
+ return;
+
+ dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ write_blk(h, blk, buf);
+ if (info->dqi_free_entry) {
+ read_blk(h, info->dqi_free_entry, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+ cpu_to_le32(blk);
+ write_blk(h, info->dqi_free_entry, tmpbuf);
+ }
+ freedqbuf(tmpbuf);
+ info->dqi_free_entry = blk;
+ mark_quotafile_info_dirty(h);
+}
+
+/* Find space for dquot */
+static unsigned int find_free_dqentry(struct quota_handle *h,
+ struct dquot *dquot, int *err)
+{
+ int blk, i;
+ struct qt_disk_dqdbheader *dh;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ char *ddquot;
+ dqbuf_t buf;
+
+ *err = 0;
+ buf = getdqbuf();
+ if (!buf) {
+ *err = -ENOMEM;
+ return 0;
+ }
+
+ dh = (struct qt_disk_dqdbheader *)buf;
+ if (info->dqi_free_entry) {
+ blk = info->dqi_free_entry;
+ read_blk(h, blk, buf);
+ } else {
+ blk = get_free_dqblk(h);
+ if (blk < 0) {
+ freedqbuf(buf);
+ *err = blk;
+ return 0;
+ }
+ memset(buf, 0, QT_BLKSIZE);
+ info->dqi_free_entry = blk;
+ mark_quotafile_info_dirty(h);
+ }
+
+ /* Block will be full? */
+ if (le16_to_cpu(dh->dqdh_entries) + 1 >=
+ qtree_dqstr_in_blk(info))
+ remove_free_dqentry(h, buf, blk);
+
+ dh->dqdh_entries =
+ cpu_to_le16(le16_to_cpu(dh->dqdh_entries) + 1);
+ /* Find free structure in block */
+ ddquot = buf + sizeof(struct qt_disk_dqdbheader);
+ for (i = 0;
+ i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
+ i++)
+ ddquot += info->dqi_entry_size;
+
+ if (i == qtree_dqstr_in_blk(info))
+ log_err("find_free_dqentry(): Data block full unexpectedly.");
+
+ write_blk(h, blk, buf);
+ dquot->dq_dqb.u.v2_mdqb.dqb_off =
+ (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+ freedqbuf(buf);
+ return blk;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
+ unsigned int * treeblk, int depth)
+{
+ dqbuf_t buf;
+ int newson = 0, newact = 0;
+ __le32 *ref;
+ unsigned int newblk;
+ int ret = 0;
+
+ log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
+ buf = getdqbuf();
+ if (!buf)
+ return -ENOMEM;
+
+ if (!*treeblk) {
+ ret = get_free_dqblk(h);
+ if (ret < 0)
+ goto out_buf;
+ *treeblk = ret;
+ memset(buf, 0, QT_BLKSIZE);
+ newact = 1;
+ } else {
+ read_blk(h, *treeblk, buf);
+ }
+
+ ref = (__le32 *) buf;
+ newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (!newblk)
+ newson = 1;
+ if (depth == QT_TREEDEPTH - 1) {
+ if (newblk)
+ log_err("Inserting already present quota entry "
+ "(block %u).",
+ ref[get_index(dquot->dq_id, depth)]);
+ newblk = find_free_dqentry(h, dquot, &ret);
+ } else {
+ ret = do_insert_tree(h, dquot, &newblk, depth + 1);
+ }
+
+ if (newson && ret >= 0) {
+ ref[get_index(dquot->dq_id, depth)] =
+ cpu_to_le32(newblk);
+ write_blk(h, *treeblk, buf);
+ } else if (newact && ret < 0) {
+ put_free_dqblk(h, buf, *treeblk);
+ }
+
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
+{
+ unsigned int tmp = QT_TREEOFF;
+
+ if (do_insert_tree(h, dquot, &tmp, 0) < 0)
+ log_err("Cannot write quota (id %u): %s",
+ (unsigned int) dquot->dq_id, strerror(errno));
+}
+
+/* Write dquot to file */
+void qtree_write_dquot(struct dquot *dquot)
+{
+ errcode_t retval;
+ unsigned int ret;
+ char *ddquot;
+ struct quota_handle *h = dquot->dq_h;
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+
+
+ log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
+ dquot->dq_dqb.u.v2_mdqb.dqb_off,
+ info->dqi_entry_size);
+ retval = quota_get_mem(info->dqi_entry_size, &ddquot);
+ if (retval) {
+ errno = ENOMEM;
+ log_err("Quota write failed (id %u): %s",
+ (unsigned int)dquot->dq_id, strerror(errno));
+ return;
+ }
+ memset(ddquot, 0, info->dqi_entry_size);
+
+ if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) {
+ dq_insert_tree(dquot->dq_h, dquot);
+ }
+ info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
+ log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
+ dquot->dq_dqb.u.v2_mdqb.dqb_off,
+ info->dqi_entry_size);
+ ret = h->write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
+ info->dqi_entry_size);
+
+ if (ret != info->dqi_entry_size) {
+ if (ret > 0)
+ errno = ENOSPC;
+ log_err("Quota write failed (id %u): %s",
+ (unsigned int)dquot->dq_id, strerror(errno));
+ }
+ quota_free_mem(&ddquot);
+}
+
+/* Free dquot entry in data block */
+static void free_dqentry(struct quota_handle *h, struct dquot *dquot,
+ unsigned int blk)
+{
+ struct qt_disk_dqdbheader *dh;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+
+ if (!buf)
+ return;
+
+ if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
+ log_err("Quota structure has offset to other block (%u) "
+ "than it should (%u).", blk,
+ (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
+ QT_BLKSIZE_BITS));
+
+ read_blk(h, blk, buf);
+ dh = (struct qt_disk_dqdbheader *)buf;
+ dh->dqdh_entries =
+ cpu_to_le16(le16_to_cpu(dh->dqdh_entries) - 1);
+
+ if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
+ remove_free_dqentry(h, buf, blk);
+ put_free_dqblk(h, buf, blk);
+ } else {
+ memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
+ ((1 << QT_BLKSIZE_BITS) - 1)),
+ 0, info->dqi_entry_size);
+
+ /* First free entry? */
+ if (le16_to_cpu(dh->dqdh_entries) ==
+ qtree_dqstr_in_blk(info) - 1)
+ /* This will also write data block */
+ insert_free_dqentry(h, buf, blk);
+ else
+ write_blk(h, blk, buf);
+ }
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
+ freedqbuf(buf);
+}
+
+/* Remove reference to dquot from tree */
+static void remove_tree(struct quota_handle *h, struct dquot *dquot,
+ unsigned int * blk, int depth)
+{
+ dqbuf_t buf = getdqbuf();
+ unsigned int newblk;
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return;
+
+ read_blk(h, *blk, buf);
+ newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (depth == QT_TREEDEPTH - 1) {
+ free_dqentry(h, dquot, newblk);
+ newblk = 0;
+ } else {
+ remove_tree(h, dquot, &newblk, depth + 1);
+ }
+
+ if (!newblk) {
+ int i;
+
+ ref[get_index(dquot->dq_id, depth)] = cpu_to_le32(0);
+
+ /* Block got empty? */
+ for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
+
+ /* Don't put the root block into the free block list */
+ if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
+ put_free_dqblk(h, buf, *blk);
+ *blk = 0;
+ } else {
+ write_blk(h, *blk, buf);
+ }
+ }
+ freedqbuf(buf);
+}
+
+/* Delete dquot from tree */
+void qtree_delete_dquot(struct dquot *dquot)
+{
+ unsigned int tmp = QT_TREEOFF;
+
+ if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
+ return;
+ remove_tree(dquot->dq_h, dquot, &tmp, 0);
+}
+
+/* Find entry in block */
+static long find_block_dqentry(struct quota_handle *h,
+ struct dquot *dquot, unsigned int blk)
+{
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+ int i;
+ char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
+
+ if (!buf)
+ return -ENOMEM;
+
+ read_blk(h, blk, buf);
+ for (i = 0;
+ i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
+ i++)
+ ddquot += info->dqi_entry_size;
+
+ if (i == qtree_dqstr_in_blk(info))
+ log_err("Quota for id %u referenced but not present.",
+ dquot->dq_id);
+ freedqbuf(buf);
+ return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+}
+
+/* Find entry for given id in the tree */
+static long find_tree_dqentry(struct quota_handle *h,
+ struct dquot *dquot,
+ unsigned int blk, int depth)
+{
+ dqbuf_t buf = getdqbuf();
+ long ret = 0;
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return -ENOMEM;
+
+ read_blk(h, blk, buf);
+ ret = 0;
+ blk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (!blk) /* No reference? */
+ goto out_buf;
+ if (depth < QT_TREEDEPTH - 1)
+ ret = find_tree_dqentry(h, dquot, blk, depth + 1);
+ else
+ ret = find_block_dqentry(h, dquot, blk);
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline long find_dqentry(struct quota_handle *h,
+ struct dquot *dquot)
+{
+ return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
+}
+
+/*
+ * Read dquot from disk.
+ */
+struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
+{
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ long offset;
+ unsigned int ret;
+ char *ddquot;
+ struct dquot *dquot = get_empty_dquot();
+
+ if (!dquot)
+ return NULL;
+ if (quota_get_mem(info->dqi_entry_size, &ddquot)) {
+ quota_free_mem(&dquot);
+ return NULL;
+ }
+
+ dquot->dq_id = id;
+ dquot->dq_h = h;
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
+ memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
+
+ offset = find_dqentry(h, dquot);
+ if (offset > 0) {
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
+ ret = h->read(&h->qh_qf, offset, ddquot,
+ info->dqi_entry_size);
+ if (ret != info->dqi_entry_size) {
+ if (ret > 0)
+ errno = EIO;
+ log_err("Cannot read quota structure for id %u: %s",
+ dquot->dq_id, strerror(errno));
+ }
+ info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
+ }
+ quota_free_mem(&ddquot);
+ return dquot;
+}
+
+/*
+ * Scan all dquots in file and call callback on each
+ */
+#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
+#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
+
+static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+ struct qt_disk_dqdbheader *dh;
+ char *ddata;
+ int entries, i;
+
+ if (!buf)
+ return 0;
+
+ set_bit(bitmap, blk);
+ read_blk(dquot->dq_h, blk, buf);
+ dh = (struct qt_disk_dqdbheader *)buf;
+ ddata = buf + sizeof(struct qt_disk_dqdbheader);
+ entries = le16_to_cpu(dh->dqdh_entries);
+ for (i = 0; i < qtree_dqstr_in_blk(info);
+ i++, ddata += info->dqi_entry_size)
+ if (!qtree_entry_unused(info, ddata)) {
+ dquot->dq_dqb.u.v2_mdqb.dqb_off =
+ (blk << QT_BLKSIZE_BITS) +
+ sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+ info->dqi_ops->disk2mem_dqblk(dquot, ddata);
+ if (process_dquot(dquot, data) < 0)
+ break;
+ }
+ freedqbuf(buf);
+ return entries;
+}
+
+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.",
+ blk,
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
+ quota_type2name(h->qh_type));
+ return -1;
+ }
+ return 0;
+}
+
+/* Return 0 for successful run */
+static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
+ char *bitmap, int *entries,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ int i;
+ dqbuf_t buf = getdqbuf();
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return -1;
+
+ read_blk(dquot->dq_h, blk, buf);
+ for (i = 0; i < QT_BLKSIZE >> 2; i++) {
+ blk = le32_to_cpu(ref[i]);
+ if (blk == 0)
+ continue;
+
+ if (check_reference(dquot->dq_h, blk))
+ break;
+
+ if (depth == QT_TREEDEPTH - 1) {
+ if (!get_bit(bitmap, blk))
+ *entries += report_block(dquot, blk, bitmap,
+ process_dquot, data);
+ } else {
+ if (report_tree(dquot, blk, depth + 1, bitmap, entries,
+ process_dquot, data))
+ break;
+ }
+ }
+ freedqbuf(buf);
+ return (i < QT_BLKSIZE >> 2) ? -1 : 0;
+}
+
+static unsigned int find_set_bits(char *bmp, int blocks)
+{
+ unsigned int used = 0;
+ int i;
+
+ for (i = 0; i < blocks; i++)
+ if (get_bit(bmp, i))
+ used++;
+ return used;
+}
+
+int qtree_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
+ struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
+ struct dquot *dquot = get_empty_dquot();
+ char *bitmap = NULL;
+ int ret = -1;
+ int entries = 0;
+
+ if (!dquot)
+ return -1;
+
+ dquot->dq_h = h;
+ if (quota_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap))
+ goto out;
+ if (report_tree(dquot, QT_TREEOFF, 0, bitmap, &entries, process_dquot,
+ data))
+ goto out;
+
+ v2info->dqi_used_entries = entries;
+ v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
+ ret = 0;
+
+out:
+ if (bitmap)
+ quota_free_mem(&bitmap);
+ if (dquot)
+ quota_free_mem(&dquot);
+
+ return ret;
+}
diff --git a/fsck/quotaio_tree.h b/fsck/quotaio_tree.h
new file mode 100644
index 0000000..aed93a8
--- /dev/null
+++ b/fsck/quotaio_tree.h
@@ -0,0 +1,70 @@
+/*
+ * Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTA_TREE_H
+#define _LINUX_QUOTA_TREE_H
+
+#include <inttypes.h>
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <sys/types.h>
+
+#include <f2fs_fs.h>
+
+typedef __u32 qid_t; /* Type in which we store ids in memory */
+
+#define QT_TREEOFF 1 /* Offset of tree in file in blocks */
+#define QT_TREEDEPTH 4 /* Depth of quota tree */
+#define QT_BLKSIZE_BITS 10
+#define QT_BLKSIZE (1 << QT_BLKSIZE_BITS) /* Size of block with quota
+ * structures */
+
+/*
+ * Structure of header of block with quota structures. It is padded to 16 bytes
+ * so there will be space for exactly 21 quota-entries in a block
+ */
+struct qt_disk_dqdbheader {
+ __le32 dqdh_next_free; /* Number of next block with free
+ * entry */
+ __le32 dqdh_prev_free; /* Number of previous block with free
+ * entry */
+ __le16 dqdh_entries; /* Number of valid entries in block */
+ __le16 dqdh_pad1;
+ __le32 dqdh_pad2;
+} __attribute__ ((packed));
+
+struct dquot;
+struct quota_handle;
+
+/* Operations */
+struct qtree_fmt_operations {
+ /* Convert given entry from in memory format to disk one */
+ void (*mem2disk_dqblk)(void *disk, struct dquot *dquot);
+ /* Convert given entry from disk format to in memory one */
+ void (*disk2mem_dqblk)(struct dquot *dquot, void *disk);
+ /* Is this structure for given id? */
+ int (*is_id)(void *disk, struct dquot *dquot);
+};
+
+/* Inmemory copy of version specific information */
+struct qtree_mem_dqinfo {
+ unsigned int dqi_blocks; /* # of blocks in quota file */
+ unsigned int dqi_free_blk; /* First block in list of free blocks */
+ unsigned int dqi_free_entry; /* First block with free entry */
+ unsigned int dqi_entry_size; /* Size of quota entry in quota file */
+ struct qtree_fmt_operations *dqi_ops; /* Operations for entry
+ * manipulation */
+};
+
+void qtree_write_dquot(struct dquot *dquot);
+struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id);
+void qtree_delete_dquot(struct dquot *dquot);
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk);
+int qtree_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *), void *data);
+
+int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info);
+
+#endif /* _LINUX_QUOTAIO_TREE_H */
diff --git a/fsck/quotaio_v2.c b/fsck/quotaio_v2.c
new file mode 100644
index 0000000..478cd17
--- /dev/null
+++ b/fsck/quotaio_v2.c
@@ -0,0 +1,284 @@
+/*
+ * Implementation of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+#include "quotaio_v2.h"
+#include "dqblk_v2.h"
+#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_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);
+static int v2_commit_dquot(struct dquot *dquot);
+static int v2_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *dquot,
+ void *data),
+ void *data);
+static int v2_report(struct quota_handle *h, int verbose);
+
+struct quotafile_ops quotafile_ops_2 = {
+ .check_file = v2_check_file,
+ .init_io = v2_init_io,
+ .new_io = v2_new_io,
+ .write_info = v2_write_info,
+ .read_dquot = v2_read_dquot,
+ .commit_dquot = v2_commit_dquot,
+ .scan_dquots = v2_scan_dquots,
+ .report = v2_report,
+};
+
+/*
+ * Copy dquot from disk to memory
+ */
+static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r1_disk_dqblk *d = dp, empty;
+
+ dquot->dq_id = le32_to_cpu(d->dqb_id);
+ m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
+ m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
+ m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
+ m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+ m->dqb_itime = le64_to_cpu(d->dqb_itime);
+ m->dqb_btime = le64_to_cpu(d->dqb_btime);
+
+ memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
+ empty.dqb_itime = cpu_to_le64(1);
+ if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
+ m->dqb_itime = 0;
+}
+
+/*
+ * Copy dquot from memory to disk
+ */
+static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r1_disk_dqblk *d = dp;
+
+ d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
+ d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
+ d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
+ d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
+ d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+ d->dqb_itime = cpu_to_le64(m->dqb_itime);
+ d->dqb_btime = cpu_to_le64(m->dqb_btime);
+ d->dqb_id = cpu_to_le32(dquot->dq_id);
+ if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
+ d->dqb_itime = cpu_to_le64(1);
+}
+
+static int v2r1_is_id(void *dp, struct dquot *dquot)
+{
+ struct v2r1_disk_dqblk *d = dp;
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ if (qtree_entry_unused(info, dp))
+ return 0;
+ return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+}
+
+static struct qtree_fmt_operations v2r1_fmt_ops = {
+ .mem2disk_dqblk = v2r1_mem2diskdqblk,
+ .disk2mem_dqblk = v2r1_disk2memdqblk,
+ .is_id = v2r1_is_id,
+};
+
+/*
+ * Copy dqinfo from disk to memory
+ */
+static inline void v2_disk2memdqinfo(struct util_dqinfo *m,
+ struct v2_disk_dqinfo *d)
+{
+ m->dqi_bgrace = le32_to_cpu(d->dqi_bgrace);
+ m->dqi_igrace = le32_to_cpu(d->dqi_igrace);
+ m->u.v2_mdqi.dqi_flags = le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
+ m->u.v2_mdqi.dqi_qtree.dqi_blocks = le32_to_cpu(d->dqi_blocks);
+ m->u.v2_mdqi.dqi_qtree.dqi_free_blk =
+ le32_to_cpu(d->dqi_free_blk);
+ m->u.v2_mdqi.dqi_qtree.dqi_free_entry =
+ le32_to_cpu(d->dqi_free_entry);
+}
+
+/*
+ * Copy dqinfo from memory to disk
+ */
+static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
+ struct util_dqinfo *m)
+{
+ d->dqi_bgrace = cpu_to_le32(m->dqi_bgrace);
+ d->dqi_igrace = cpu_to_le32(m->dqi_igrace);
+ d->dqi_flags = cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
+ d->dqi_blocks = cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
+ d->dqi_free_blk =
+ cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
+ d->dqi_free_entry =
+ cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
+}
+
+static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh)
+{
+ if (h->read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) !=
+ sizeof(struct v2_disk_dqheader))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Check whether given quota file is in our format
+ */
+static int v2_check_file(struct quota_handle *h, int type)
+{
+ struct v2_disk_dqheader dqh;
+ int file_magics[] = INITQMAGICS;
+ int be_magic;
+
+ if (!v2_read_header(h, &dqh))
+ return 0;
+
+ be_magic = be32_to_cpu((__force __be32)dqh.dqh_magic);
+ if (be_magic == file_magics[type]) {
+ log_err("Your quota file is stored in wrong endianity");
+ return 0;
+ }
+ if (V2_VERSION != le32_to_cpu(dqh.dqh_version))
+ return 0;
+ return 1;
+}
+
+/*
+ * Open quotafile
+ */
+static int v2_init_io(struct quota_handle *h)
+{
+ struct v2_disk_dqinfo ddqinfo;
+
+ 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))
+ return -1;
+ v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
+ return 0;
+}
+
+/*
+ * Initialize new quotafile
+ */
+static int v2_new_io(struct quota_handle *h)
+{
+ int file_magics[] = INITQMAGICS;
+ struct v2_disk_dqheader ddqheader;
+ struct v2_disk_dqinfo ddqinfo;
+
+ if (h->qh_fmt != QFMT_VFS_V1)
+ return -1;
+
+ /* Write basic quota header */
+ ddqheader.dqh_magic = cpu_to_le32(file_magics[h->qh_type]);
+ ddqheader.dqh_version = cpu_to_le32(V2_VERSION);
+ if (h->write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) !=
+ sizeof(ddqheader))
+ return -1;
+
+ /* Write information about quotafile */
+ h->qh_info.dqi_bgrace = MAX_DQ_TIME;
+ h->qh_info.dqi_igrace = MAX_IQ_TIME;
+ h->qh_info.u.v2_mdqi.dqi_flags = 0;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
+ 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;
+ v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
+ if (h->write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
+ sizeof(ddqinfo)) !=
+ sizeof(ddqinfo))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Write information (grace times to file)
+ */
+static int v2_write_info(struct quota_handle *h)
+{
+ struct v2_disk_dqinfo ddqinfo;
+
+ v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
+ if (h->write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) !=
+ sizeof(ddqinfo))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Read dquot from disk
+ */
+static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id)
+{
+ return qtree_read_dquot(h, id);
+}
+
+/*
+ * Commit changes of dquot to disk - it might also mean deleting it when quota
+ * became fake one and user has no blocks.
+ * User can process use 'errno' to detect errstr.
+ */
+static int v2_commit_dquot(struct dquot *dquot)
+{
+ struct util_dqblk *b = &dquot->dq_dqb;
+
+ if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit &&
+ !b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit)
+ {
+ qtree_delete_dquot(dquot);
+ } else {
+ qtree_write_dquot(dquot);
+ }
+ return 0;
+}
+
+static int v2_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ return qtree_scan_dquots(h, process_dquot, data);
+}
+
+/* Report information about quotafile.
+ * TODO: Not used right now, but we should be able to use this when we add
+ * support to debugfs to read quota files.
+ */
+static int v2_report(struct quota_handle *UNUSED(h), int UNUSED(verbose))
+{
+ log_err("Not Implemented.");
+ return -1;
+}
diff --git a/fsck/quotaio_v2.h b/fsck/quotaio_v2.h
new file mode 100644
index 0000000..de2db27
--- /dev/null
+++ b/fsck/quotaio_v2.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Header file for disk format of new quotafile format
+ *
+ */
+
+#ifndef GUARD_QUOTAIO_V2_H
+#define GUARD_QUOTAIO_V2_H
+
+#include <sys/types.h>
+#include "quotaio.h"
+
+/* Offset of info header in file */
+#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader)
+/* Supported version of quota-tree format */
+#define V2_VERSION 1
+
+struct v2_disk_dqheader {
+ __le32 dqh_magic; /* Magic number identifying file */
+ __le32 dqh_version; /* File version */
+} __attribute__ ((packed));
+
+/* Flags for version specific files */
+#define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+ __le32 dqi_bgrace; /* Time before block soft limit becomes
+ * hard limit */
+ __le32 dqi_igrace; /* Time before inode soft limit becomes
+ * hard limit */
+ __le32 dqi_flags; /* Flags for quotafile (DQF_*) */
+ __le32 dqi_blocks; /* Number of blocks in file */
+ __le32 dqi_free_blk; /* Number of first free block in the list */
+ __le32 dqi_free_entry; /* Number of block with at least one
+ * free entry */
+} __attribute__ ((packed));
+
+struct v2r1_disk_dqblk {
+ __le32 dqb_id; /* id this quota applies to */
+ __le32 dqb_pad;
+ __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __le64 dqb_isoftlimit; /* preferred inode limit */
+ __le64 dqb_curinodes; /* current # allocated inodes */
+ __le64 dqb_bhardlimit; /* absolute limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_bsoftlimit; /* preferred limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_curspace; /* current space occupied (in bytes) */
+ __le64 dqb_btime; /* time limit for excessive disk use */
+ __le64 dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+#endif
diff --git a/fsck/resize.c b/fsck/resize.c
index 4584d6f..6c3eeab 100644
--- a/fsck/resize.c
+++ b/fsck/resize.c
@@ -36,7 +36,7 @@ static int get_new_sb(struct f2fs_super_block *sb)
zone_align_start_offset) / segment_size_bytes /
c.segs_per_sec * c.segs_per_sec);
- blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
+ blocks_for_sit = SIZE_ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
sit_segments = SEG_ALIGN(blocks_for_sit);
set_sb(segment_count_sit, sit_segments * 2);
set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
@@ -45,7 +45,8 @@ static int get_new_sb(struct f2fs_super_block *sb)
total_valid_blks_available = (get_sb(segment_count) -
(get_sb(segment_count_ckpt) +
get_sb(segment_count_sit))) * blks_per_seg;
- blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+ blocks_for_nat = SIZE_ALIGN(total_valid_blks_available,
+ NAT_ENTRY_PER_BLOCK);
set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
diff --git a/fsck/segment.c b/fsck/segment.c
index 6b2f6c1..4f8bdb4 100644
--- a/fsck/segment.c
+++ b/fsck/segment.c
@@ -16,12 +16,21 @@
#include "fsck.h"
#include "node.h"
+static void write_inode(u64 blkaddr, struct f2fs_node *inode)
+{
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ inode->i.i_inode_checksum =
+ cpu_to_le32(f2fs_inode_chksum(inode));
+ ASSERT(dev_write_block(inode, blkaddr) >= 0);
+}
+
void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
struct f2fs_summary *sum, int type)
{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct seg_entry *se;
- u64 blkaddr;
- u64 offset;
+ u64 blkaddr, offset;
+ u64 old_blkaddr = *to;
blkaddr = SM_I(sbi)->main_blkaddr;
@@ -35,7 +44,16 @@ void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
se->type = type;
se->valid_blocks++;
f2fs_set_bit(offset, (char *)se->cur_valid_map);
- sbi->total_valid_block_count++;
+ if (c.func == FSCK) {
+ f2fs_set_main_bitmap(sbi, blkaddr, type);
+ f2fs_set_sit_bitmap(sbi, blkaddr);
+ }
+
+ if (old_blkaddr == NULL_ADDR) {
+ sbi->total_valid_block_count++;
+ if (c.func == FSCK)
+ fsck->chk.valid_blk_cnt++;
+ }
se->dirty = 1;
/* read/write SSA */
@@ -48,6 +66,7 @@ void new_data_block(struct f2fs_sb_info *sbi, void *block,
{
struct f2fs_summary sum;
struct node_info ni;
+ unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
ASSERT(dn->node_blk);
memset(block, 0, BLOCK_SZ);
@@ -56,110 +75,200 @@ void new_data_block(struct f2fs_sb_info *sbi, void *block,
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
reserve_new_block(sbi, &dn->data_blkaddr, &sum, type);
- inc_inode_blocks(dn);
+ if (blkaddr == NULL_ADDR)
+ inc_inode_blocks(dn);
+ else if (blkaddr == NEW_ADDR)
+ dn->idirty = 1;
set_data_blkaddr(dn);
}
-static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
u64 count, pgoff_t offset)
{
- u64 start = F2FS_BYTES_TO_BLK(offset);
- u64 len = F2FS_BYTES_TO_BLK(count);
- u64 end_offset;
- u64 off_in_block, len_in_block, len_already;
- struct dnode_of_data dn = {0};
- void *data_blk;
+ struct dnode_of_data dn;
struct node_info ni;
struct f2fs_node *inode;
- int ret = -1;
-
+ char *blk_buffer;
+ u64 filesize;
+ u64 off_in_blk;
+ u64 len_in_blk;
+ u64 read_count;
+ u64 remained_blkentries;
+ block_t blkaddr;
+ void *index_node = NULL;
+
+ memset(&dn, 0, sizeof(dn));
+
+ /* Memory allocation for block buffer and inode. */
+ blk_buffer = calloc(BLOCK_SZ, 2);
+ ASSERT(blk_buffer);
+ inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ);
+
+ /* Read inode */
get_node_info(sbi, ino, &ni);
- inode = calloc(BLOCK_SZ, 1);
- ASSERT(inode);
-
- ret = dev_read_block(inode, ni.blk_addr);
- ASSERT(ret >= 0);
-
- if (S_ISDIR(le16_to_cpu(inode->i.i_mode)) ||
- S_ISLNK(le16_to_cpu(inode->i.i_mode)))
- ASSERT(0);
-
- off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1);
- len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block;
- len_already = 0;
-
- /*
- * When calculate how many blocks this 'count' stride accross,
- * We should take offset in a block in account.
- */
- len = F2FS_BYTES_TO_BLK(count + off_in_block
- + ((1 << F2FS_BLKSIZE_BITS) - 1));
-
- data_blk = calloc(BLOCK_SZ, 1);
- ASSERT(data_blk);
+ ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+ ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+ ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
+
+ /* Adjust count with file length. */
+ filesize = le64_to_cpu(inode->i.i_size);
+ if (offset > filesize)
+ count = 0;
+ else if (count + offset > filesize)
+ count = filesize - offset;
+
+ /* Main loop for file blocks */
+ read_count = remained_blkentries = 0;
+ while (count > 0) {
+ if (remained_blkentries == 0) {
+ set_new_dnode(&dn, inode, NULL, ino);
+ get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset),
+ LOOKUP_NODE);
+ if (index_node)
+ free(index_node);
+ index_node = (dn.node_blk == dn.inode_blk) ?
+ NULL : dn.node_blk;
+ remained_blkentries = ADDRS_PER_PAGE(dn.node_blk);
+ }
+ ASSERT(remained_blkentries > 0);
+
+ blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+ if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR)
+ break;
+
+ off_in_blk = offset % BLOCK_SZ;
+ len_in_blk = BLOCK_SZ - off_in_blk;
+ if (len_in_blk > count)
+ len_in_blk = count;
+
+ /* Read data from single block. */
+ if (len_in_blk < BLOCK_SZ) {
+ ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0);
+ memcpy(buffer, blk_buffer + off_in_blk, len_in_blk);
+ } else {
+ /* Direct read */
+ ASSERT(dev_read_block(buffer, blkaddr) >= 0);
+ }
- set_new_dnode(&dn, inode, NULL, ino);
+ offset += len_in_blk;
+ count -= len_in_blk;
+ buffer += len_in_blk;
+ read_count += len_in_blk;
- while (len) {
- if (dn.node_blk != dn.inode_blk)
- free(dn.node_blk);
+ dn.ofs_in_node++;
+ remained_blkentries--;
+ }
+ if (index_node)
+ free(index_node);
+ free(blk_buffer);
- set_new_dnode(&dn, inode, NULL, ino);
- get_dnode_of_data(sbi, &dn, start, ALLOC_NODE);
+ return read_count;
+}
- end_offset = ADDRS_PER_PAGE(dn.node_blk);
+u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
+ u64 count, pgoff_t offset)
+{
+ struct dnode_of_data dn;
+ struct node_info ni;
+ struct f2fs_node *inode;
+ char *blk_buffer;
+ u64 off_in_blk;
+ u64 len_in_blk;
+ u64 written_count;
+ u64 remained_blkentries;
+ block_t blkaddr;
+ void* index_node = NULL;
+ int idirty = 0;
+
+ /* Memory allocation for block buffer and inode. */
+ blk_buffer = calloc(BLOCK_SZ, 2);
+ ASSERT(blk_buffer);
+ inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ);
+
+ /* Read inode */
+ get_node_info(sbi, ino, &ni);
+ ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+ ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+ ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
+
+ /* Main loop for file blocks */
+ written_count = remained_blkentries = 0;
+ while (count > 0) {
+ if (remained_blkentries == 0) {
+ set_new_dnode(&dn, inode, NULL, ino);
+ get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset),
+ ALLOC_NODE);
+ idirty |= dn.idirty;
+ if (index_node)
+ free(index_node);
+ index_node = (dn.node_blk == dn.inode_blk) ?
+ NULL : dn.node_blk;
+ remained_blkentries = ADDRS_PER_PAGE(dn.node_blk);
+ }
+ ASSERT(remained_blkentries > 0);
- while (dn.ofs_in_node < end_offset && len) {
- block_t blkaddr;
+ blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+ if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
+ new_data_block(sbi, blk_buffer, &dn, CURSEG_WARM_DATA);
+ blkaddr = dn.data_blkaddr;
+ }
- blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+ off_in_blk = offset % BLOCK_SZ;
+ len_in_blk = BLOCK_SZ - off_in_blk;
+ if (len_in_blk > count)
+ len_in_blk = count;
+
+ /* Write data to single block. */
+ if (len_in_blk < BLOCK_SZ) {
+ ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0);
+ memcpy(blk_buffer + off_in_blk, buffer, len_in_blk);
+ ASSERT(dev_write_block(blk_buffer, blkaddr) >= 0);
+ } else {
+ /* Direct write */
+ ASSERT(dev_write_block(buffer, blkaddr) >= 0);
+ }
- /* A new page from WARM_DATA */
- if (blkaddr == NULL_ADDR)
- new_data_block(sbi, data_blk, &dn,
- CURSEG_WARM_DATA);
+ offset += len_in_blk;
+ count -= len_in_blk;
+ buffer += len_in_blk;
+ written_count += len_in_blk;
- /* Copy data from buffer to file */
- ret = dev_read_block(data_blk, dn.data_blkaddr);
- ASSERT(ret >= 0);
+ dn.ofs_in_node++;
+ if ((--remained_blkentries == 0 || count == 0) && (dn.ndirty))
+ ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr) >= 0);
+ }
+ if (offset > le64_to_cpu(inode->i.i_size)) {
+ inode->i.i_size = cpu_to_le64(offset);
+ idirty = 1;
+ }
+ if (idirty) {
+ ASSERT(inode == dn.inode_blk);
+ write_inode(ni.blk_addr, inode);
+ }
+ if (index_node)
+ free(index_node);
+ free(blk_buffer);
- memcpy(data_blk + off_in_block, buffer, len_in_block);
+ return written_count;
+}
- ret = dev_write_block(data_blk, dn.data_blkaddr);
- ASSERT(ret >= 0);
+/* This function updates only inode->i.i_size */
+void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize)
+{
+ struct node_info ni;
+ struct f2fs_node *inode;
- off_in_block = 0;
- len_already += len_in_block;
- if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS))
- len_in_block = 1 << F2FS_BLKSIZE_BITS;
- else
- len_in_block = count - len_already;
- len--;
- start++;
- dn.ofs_in_node++;
- }
- /* Update the direct node */
- if (dn.ndirty) {
- ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
- ASSERT(ret >= 0);
- }
- }
+ inode = calloc(BLOCK_SZ, 1);
+ ASSERT(inode);
+ get_node_info(sbi, ino, &ni);
- /* Update the inode info */
- if (le64_to_cpu(inode->i.i_size) < offset + count) {
- inode->i.i_size = cpu_to_le64(offset + count);
- dn.idirty = 1;
- }
+ ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+ ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+ ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
- if (dn.idirty) {
- ASSERT(inode == dn.inode_blk);
- ret = dev_write_block(inode, ni.blk_addr);
- ASSERT(ret >= 0);
- }
+ inode->i.i_size = cpu_to_le64(filesize);
- if (dn.node_blk && dn.node_blk != dn.inode_blk)
- free(dn.node_blk);
- free(data_blk);
+ write_inode(ni.blk_addr, inode);
free(inode);
}
@@ -167,7 +276,7 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
{
int fd, n;
pgoff_t off = 0;
- char buffer[BLOCK_SZ];
+ u8 buffer[BLOCK_SZ];
if (de->ino == 0)
return -1;
@@ -179,7 +288,7 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
}
/* inline_data support */
- if (de->size <= MAX_INLINE_DATA) {
+ if (de->size <= DEF_MAX_INLINE_DATA) {
struct node_info ni;
struct f2fs_node *node_blk;
int ret;
@@ -194,18 +303,21 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
node_blk->i.i_inline |= F2FS_INLINE_DATA;
node_blk->i.i_inline |= F2FS_DATA_EXIST;
- n = read(fd, buffer, BLOCK_SZ);
- ASSERT(n == de->size);
- memcpy(&node_blk->i.i_addr[1], buffer, de->size);
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ node_blk->i.i_inline |= F2FS_EXTRA_ATTR;
+ node_blk->i.i_extra_isize =
+ cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE);
+ }
+ n = read(fd, buffer, BLOCK_SZ);
+ ASSERT((unsigned long)n == de->size);
+ memcpy(inline_data_addr(node_blk), buffer, de->size);
node_blk->i.i_size = cpu_to_le64(de->size);
-
- ret = dev_write_block(node_blk, ni.blk_addr);
- ASSERT(ret >= 0);
+ write_inode(ni.blk_addr, node_blk);
free(node_blk);
} else {
while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
- f2fs_write_block(sbi, de->ino, buffer, n, off);
+ f2fs_write(sbi, de->ino, buffer, n, off);
off += n;
}
}
@@ -216,6 +328,11 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
update_free_segments(sbi);
- MSG(1, "Info: built a file %s, size=%lu\n", de->full_path, de->size);
+ MSG(1, "Info: Create %s -> %s\n"
+ " -- ino=%x, type=%x, mode=%x, uid=%x, "
+ "gid=%x, cap=%"PRIx64", size=%lu, 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);
return 0;
}
diff --git a/fsck/sload.c b/fsck/sload.c
index 68799c1..2842f2c 100644
--- a/fsck/sload.c
+++ b/fsck/sload.c
@@ -15,33 +15,25 @@
#include "fsck.h"
#include <libgen.h>
#include <dirent.h>
+#ifdef HAVE_MNTENT_H
#include <mntent.h>
+#endif
#ifdef HAVE_LIBSELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+static struct selabel_handle *sehnd = NULL;
#endif
+typedef void (*fs_config_f)(const char *path, int dir,
+ const char *target_out_path,
+ unsigned *uid, unsigned *gid,
+ unsigned *mode, uint64_t *capabilities);
+
+static fs_config_f fs_config_func = NULL;
+
#ifdef WITH_ANDROID
-#include <selinux/label.h>
+#include <selinux/android.h>
#include <private/android_filesystem_config.h>
-
-static void handle_selabel(struct dentry *de, int dir, char *target_out)
-{
- uint64_t capabilities;
- unsigned int mode = 0;
- unsigned int uid = 0;
- unsigned int gid = 0;
-
- fs_config(de->path, dir, target_out, &uid,
- &gid, &mode, &capabilities);
- de->mode = mode;
- de->uid = uid;
- de->gid = gid;
- de->capabilities = capabilities;
-}
-#else
-#define handle_selabel(...)
+#include <private/canned_fs_config.h>
#endif
static int filter_dot(const struct dirent *d)
@@ -64,14 +56,123 @@ static void f2fs_make_directory(struct f2fs_sb_info *sbi,
}
}
+#ifdef HAVE_LIBSELINUX
+static int set_selinux_xattr(struct f2fs_sb_info *sbi, const char *path,
+ nid_t ino, int mode)
+{
+ char *secontext = NULL;
+ char *mnt_path = NULL;
+
+ if (!sehnd)
+ return 0;
+
+ if (asprintf(&mnt_path, "%s%s", c.mount_point, path) <= 0) {
+ ERR_MSG("cannot allocate security path for %s%s\n",
+ c.mount_point, path);
+ return -ENOMEM;
+ }
+
+ /* set root inode selinux context */
+ if (selabel_lookup(sehnd, &secontext, mnt_path, mode) < 0) {
+ ERR_MSG("cannot lookup security context for %s\n", mnt_path);
+ free(mnt_path);
+ return -EINVAL;
+ }
+
+ if (secontext) {
+ MSG(2, "%s (%d) -> SELinux context = %s\n",
+ mnt_path, ino, secontext);
+ inode_set_selinux(sbi, ino, secontext);
+ }
+ freecon(secontext);
+ free(mnt_path);
+ return 0;
+}
+#else
+#define set_selinux_xattr(...) 0
+#endif
+
+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;
+
+ if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) {
+ ERR_MSG("cannot allocate mount path for %s%s\n",
+ c.mount_point, de->path);
+ return -ENOMEM;
+ }
+
+ /* Permissions */
+ if (fs_config_func != NULL) {
+ fs_config_func(mnt_path, S_ISDIR(de->mode),
+ c.target_out_dir, &uid, &gid, &imode,
+ &capabilities);
+ de->uid = uid & 0xffff;
+ de->gid = gid & 0xffff;
+ de->mode = (de->mode & S_IFMT) | (imode & 0xffff);
+ de->capabilities = capabilities;
+ }
+ MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, "
+ "capabilities = 0x%"PRIx64"\n",
+ mnt_path, de->mode, de->uid, de->gid, de->capabilities);
+ free(mnt_path);
+ return 0;
+}
+
+static void set_inode_metadata(struct dentry *de)
+{
+ struct stat stat;
+ int ret;
+
+ ret = lstat(de->full_path, &stat);
+ if (ret < 0) {
+ ERR_MSG("lstat failure\n");
+ ASSERT(0);
+ }
+
+ if (S_ISREG(stat.st_mode)) {
+ de->file_type = F2FS_FT_REG_FILE;
+ } else if (S_ISDIR(stat.st_mode)) {
+ de->file_type = F2FS_FT_DIR;
+ } else if (S_ISCHR(stat.st_mode)) {
+ de->file_type = F2FS_FT_CHRDEV;
+ } else if (S_ISBLK(stat.st_mode)) {
+ de->file_type = F2FS_FT_BLKDEV;
+ } else if (S_ISFIFO(stat.st_mode)) {
+ de->file_type = F2FS_FT_FIFO;
+ } else if (S_ISSOCK(stat.st_mode)) {
+ de->file_type = F2FS_FT_SOCK;
+ } else if (S_ISLNK(stat.st_mode)) {
+ de->file_type = F2FS_FT_SYMLINK;
+ de->link = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(de->link);
+ ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1);
+ ASSERT(ret >= 0);
+ } else {
+ ERR_MSG("unknown file type on %s", de->path);
+ ASSERT(0);
+ }
+
+ de->size = stat.st_size;
+ de->mode = stat.st_mode &
+ (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
+ if (c.fixed_time == -1 && c.from_dir)
+ de->mtime = stat.st_mtime;
+ else
+ de->mtime = c.fixed_time;
+
+ set_perms_and_caps(de);
+}
+
static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
const char *dir_path, const char *target_out_dir,
- nid_t dir_ino, struct selabel_handle *sehnd)
+ nid_t dir_ino)
{
int entries = 0;
struct dentry *dentries;
struct dirent **namelist = NULL;
- struct stat stat;
int i, ret = 0;
entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
@@ -92,7 +193,7 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
}
dentries[i].len = strlen((char *)dentries[i].name);
- ret = asprintf(&dentries[i].path, "%s/%s",
+ ret = asprintf(&dentries[i].path, "%s%s",
dir_path, namelist[i]->d_name);
ASSERT(ret > 0);
ret = asprintf(&dentries[i].full_path, "%s/%s",
@@ -100,52 +201,9 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
ASSERT(ret > 0);
free(namelist[i]);
- ret = lstat(dentries[i].full_path, &stat);
- if (ret < 0) {
- ERR_MSG("Skip: lstat failure\n");
- continue;
- }
- dentries[i].size = stat.st_size;
- dentries[i].mode = stat.st_mode &
- (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
- dentries[i].mtime = stat.st_mtime;
-
- handle_selabel(dentries + i, S_ISDIR(stat.st_mode),
- target_out_dir);
-
-#ifdef HAVE_LIBSELINUX
- if (sehnd && selabel_lookup(sehnd, &dentries[i].secon,
- dentries[i].path, stat.st_mode) < 0)
- ERR_MSG("Cannot lookup security context for %s\n",
- dentries[i].path);
-#endif
+ set_inode_metadata(dentries + i);
dentries[i].pino = dir_ino;
-
- if (S_ISREG(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_REG_FILE;
- } else if (S_ISDIR(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_DIR;
- } else if (S_ISCHR(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_CHRDEV;
- } else if (S_ISBLK(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_BLKDEV;
- } else if (S_ISFIFO(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_FIFO;
- } else if (S_ISSOCK(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_SOCK;
- } else if (S_ISLNK(stat.st_mode)) {
- dentries[i].file_type = F2FS_FT_SYMLINK;
- dentries[i].link = calloc(F2FS_BLKSIZE, 1);
- ASSERT(dentries[i].link);
- ret = readlink(dentries[i].full_path,
- dentries[i].link, F2FS_BLKSIZE - 1);
- ASSERT(ret >= 0);
- } else {
- MSG(1, "unknown file type on %s", dentries[i].path);
- i--;
- entries--;
- }
}
free(namelist);
@@ -157,9 +215,9 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
f2fs_build_file(sbi, dentries + i);
} else if (dentries[i].file_type == F2FS_FT_DIR) {
char *subdir_full_path = NULL;
- char *subdir_dir_path;
+ char *subdir_dir_path = NULL;
- ret = asprintf(&subdir_full_path, "%s/",
+ ret = asprintf(&subdir_full_path, "%s",
dentries[i].full_path);
ASSERT(ret > 0);
ret = asprintf(&subdir_dir_path, "%s/",
@@ -167,7 +225,7 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
ASSERT(ret > 0);
build_directory(sbi, subdir_full_path, subdir_dir_path,
- target_out_dir, dentries[i].ino, sehnd);
+ target_out_dir, dentries[i].ino);
free(subdir_full_path);
free(subdir_dir_path);
} else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
@@ -179,19 +237,10 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
MSG(1, "Error unknown file type\n");
}
-#ifdef HAVE_LIBSELINUX
- if (dentries[i].secon) {
- inode_set_selinux(sbi, dentries[i].ino, dentries[i].secon);
- MSG(1, "File = %s \n----->SELinux context = %s\n",
- dentries[i].path, dentries[i].secon);
- MSG(1, "----->mode = 0x%x, uid = 0x%x, gid = 0x%x, "
- "capabilities = 0x%lx \n",
- dentries[i].mode, dentries[i].uid,
- dentries[i].gid, dentries[i].capabilities);
- }
-
- free(dentries[i].secon);
-#endif
+ ret = set_selinux_xattr(sbi, dentries[i].path,
+ dentries[i].ino, dentries[i].mode);
+ if (ret)
+ return ret;
free(dentries[i].path);
free(dentries[i].full_path);
@@ -202,47 +251,71 @@ static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
return 0;
}
-int f2fs_sload(struct f2fs_sb_info *sbi, const char *from_dir,
- const char *mount_point,
- const char *target_out_dir,
- struct selabel_handle *sehnd)
+static int configure_files(void)
{
- int ret = 0;
- nid_t mnt_ino = F2FS_ROOT_INO(sbi);
+#ifdef HAVE_LIBSELINUX
+ if (!c.nr_opt)
+ goto skip;
+#if !defined(__ANDROID__)
+ sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt);
+ if (!sehnd) {
+ ERR_MSG("Failed to open file contexts \"%s\"",
+ c.seopt_file[0].value);
+ return -EINVAL;
+ }
+#else
+ sehnd = selinux_android_file_context_handle();
+ if (!sehnd) {
+ ERR_MSG("Failed to get android file_contexts\n", c.mount_point);
+ return -EINVAL;
+ }
+#endif
+skip:
+#endif
+#ifdef WITH_ANDROID
+ /* Load the FS config */
+ if (c.fs_config_file) {
+ int ret = load_canned_fs_config(c.fs_config_file);
- /* flush NAT/SIT journal entries */
- flush_journal_entries(sbi);
+ if (ret < 0) {
+ ERR_MSG("Failed to load fs_config \"%s\"",
+ c.fs_config_file);
+ return ret;
+ }
+ fs_config_func = canned_fs_config;
+ } else {
+ fs_config_func = fs_config;
+ }
+#endif
+ return 0;
+}
- ret = f2fs_find_path(sbi, (char *)mount_point, &mnt_ino);
+int f2fs_sload(struct f2fs_sb_info *sbi)
+{
+ int ret = 0;
+
+ ret = configure_files();
if (ret) {
- ERR_MSG("Failed to get mount point %s\n", mount_point);
+ ERR_MSG("Failed to configure files\n");
return ret;
}
- ret = build_directory(sbi, from_dir, mount_point, target_out_dir,
- mnt_ino, sehnd);
+ /* flush NAT/SIT journal entries */
+ flush_journal_entries(sbi);
+
+ ret = build_directory(sbi, c.from_dir, "/",
+ c.target_out_dir, F2FS_ROOT_INO(sbi));
if (ret) {
ERR_MSG("Failed to build due to %d\n", ret);
return ret;
}
-#ifdef HAVE_LIBSELINUX
- if (sehnd) {
- char *secontext = NULL;
-
- /* set root inode selinux context */
- if (selabel_lookup(sehnd, &secontext, mount_point, S_IFDIR) < 0)
- ERR_MSG("cannot lookup security context for %s\n",
- mount_point);
- if (secontext) {
- MSG(1, "Labeling %s as %s, root_ino = %d\n",
- mount_point, secontext, F2FS_ROOT_INO(sbi));
- /* xattr_add for root inode */
- inode_set_selinux(sbi, F2FS_ROOT_INO(sbi), secontext);
- }
- free(secontext);
+ ret = set_selinux_xattr(sbi, c.mount_point,
+ F2FS_ROOT_INO(sbi), S_IFDIR);
+ if (ret) {
+ ERR_MSG("Failed to set selinux for root: %d\n", ret);
+ return ret;
}
-#endif
/* update curseg info; can update sit->types */
move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
diff --git a/fsck/xattr.c b/fsck/xattr.c
index 3f5c7d3..1d15d1b 100644
--- a/fsck/xattr.c
+++ b/fsck/xattr.c
@@ -17,10 +17,7 @@
#include "node.h"
#include "xattr.h"
-#define XATTR_CREATE 0x1
-#define XATTR_REPLACE 0x2
-
-static void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
{
struct f2fs_xattr_header *header;
void *txattr_addr;
@@ -80,7 +77,6 @@ static void write_all_xattrs(struct f2fs_sb_info *sbi,
u64 inline_size = inline_xattr_size(&inode->i);
int ret;
- ASSERT(inode->i.i_inline & F2FS_INLINE_XATTR);
memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size);
if (hsize <= inline_size)
diff --git a/fsck/xattr.h b/fsck/xattr.h
index b414629..e4a98e2 100644
--- a/fsck/xattr.h
+++ b/fsck/xattr.h
@@ -17,6 +17,9 @@
#define _XATTR_H_
#include "f2fs.h"
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
struct f2fs_xattr_header {
__le32 h_magic; /* magic number for identification */
@@ -31,10 +34,79 @@ struct f2fs_xattr_entry {
char e_name[0]; /* attribute name */
};
+#define FS_KEY_DESCRIPTOR_SIZE 8
+#define FS_KEY_DERIVATION_NONCE_SIZE 16
+
+struct fscrypt_context {
+ u8 format;
+ u8 contents_encryption_mode;
+ u8 filenames_encryption_mode;
+ u8 flags;
+ u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
+} __attribute__((packed));
+
+#define F2FS_ACL_VERSION 0x0001
+
+struct f2fs_acl_entry {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_id;
+};
+
+struct f2fs_acl_entry_short {
+ __le16 e_tag;
+ __le16 e_perm;
+};
+
+struct f2fs_acl_header {
+ __le32 a_version;
+};
+
+static inline int f2fs_acl_count(int size)
+{
+ ssize_t s;
+ size -= sizeof(struct f2fs_acl_header);
+ s = size - 4 * sizeof(struct f2fs_acl_entry_short);
+ if (s < 0) {
+ if (size % sizeof(struct f2fs_acl_entry_short))
+ return -1;
+ return size / sizeof(struct f2fs_acl_entry_short);
+ } else {
+ if (s % sizeof(struct f2fs_acl_entry))
+ return -1;
+ return s / sizeof(struct f2fs_acl_entry) + 4;
+ }
+}
+
+#ifndef XATTR_USER_PREFIX
+#define XATTR_USER_PREFIX "user."
+#endif
+#ifndef XATTR_SECURITY_PREFIX
+#define XATTR_SECURITY_PREFIX "security."
+#endif
+#ifndef XATTR_TRUSTED_PREFIX
+#define XATTR_TRUSTED_PREFIX "trusted."
+#endif
+
+#ifndef XATTR_CREATE
+#define XATTR_CREATE 0x1
+#endif
+#ifndef XATTR_REPLACE
+#define XATTR_REPLACE 0x2
+#endif
+
#define XATTR_ROUND (3)
#define XATTR_SELINUX_SUFFIX "selinux"
-#define F2FS_XATTR_INDEX_SECURITY 6
+#define F2FS_XATTR_INDEX_USER 1
+#define F2FS_XATTR_INDEX_POSIX_ACL_ACCESS 2
+#define F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT 3
+#define F2FS_XATTR_INDEX_TRUSTED 4
+#define F2FS_XATTR_INDEX_LUSTRE 5
+#define F2FS_XATTR_INDEX_SECURITY 6
+#define F2FS_XATTR_INDEX_ENCRYPTION 9
+
#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr))
diff --git a/include/android_config.h b/include/android_config.h
new file mode 100644
index 0000000..6f49f90
--- /dev/null
+++ b/include/android_config.h
@@ -0,0 +1,66 @@
+#if defined(__linux__)
+#define HAVE_BYTESWAP_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FALLOC_H 1
+#define HAVE_LINUX_HDREG_H 1
+#define HAVE_LINUX_LIMITS_H 1
+#define HAVE_POSIX_ACL_H 1
+#define HAVE_LINUX_TYPES_H 1
+#define HAVE_LINUX_XATTR_H 1
+#define HAVE_MNTENT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_SYSCALL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_SYSMACROS_H 1
+#define HAVE_SYS_XATTR_H 1
+#define HAVE_UNISTD_H 1
+
+#define HAVE_ADD_KEY 1
+#define HAVE_FALLOCATE 1
+#define HAVE_FSETXATTR 1
+#define HAVE_FSTAT 1
+#define HAVE_FSTAT64 1
+#define HAVE_GETMNTENT 1
+#define HAVE_KEYCTL 1
+#define HAVE_LLSEEK 1
+#define HAVE_LSEEK64 1
+#define HAVE_MEMSET 1
+#define HAVE_SETMNTENT 1
+
+#ifdef WITH_SLOAD
+#define HAVE_LIBSELINUX 1
+#endif
+#endif
+
+#if defined(__APPLE__)
+#define HAVE_FCNTL_H 1
+#define HAVE_FALLOC_H 1
+#define HAVE_POSIX_ACL_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_SYSCALL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_XATTR_H 1
+#define HAVE_UNISTD_H 1
+
+#define HAVE_ADD_KEY 1
+#define HAVE_FALLOCATE 1
+#define HAVE_FSETXATTR 1
+#define HAVE_FSTAT 1
+#define HAVE_FSTAT64 1
+#define HAVE_GETMNTENT 1
+#define HAVE_KEYCTL 1
+#define HAVE_LLSEEK 1
+#define HAVE_MEMSET 1
+
+#ifdef WITH_SLOAD
+#define HAVE_LIBSELINUX 1
+#endif
+#endif
+
+#if defined(_WIN32)
+#define HAVE_LSEEK64
+#endif
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index dd2635b..782c936 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -12,28 +12,80 @@
#ifndef __F2FS_FS_H__
#define __F2FS_FS_H__
-#include <inttypes.h>
-#include <linux/types.h>
-#include <sys/types.h>
-
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#ifdef __ANDROID__
+#define WITH_ANDROID
+#endif
+
+#ifdef WITH_ANDROID
+#include <android_config.h>
+#else
+#define WITH_DUMP
+#define WITH_DEFRAG
+#define WITH_RESIZE
+#define WITH_SLOAD
+#endif
+
+#include <inttypes.h>
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <sys/types.h>
+
#ifdef HAVE_LINUX_BLKZONED_H
#include <linux/blkzoned.h>
#endif
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) x
+#else
+# define UNUSED(x) x
+#endif
+
+#ifdef ANDROID_WINDOWS_HOST
+#undef HAVE_LINUX_TYPES_H
+typedef uint64_t u_int64_t;
+typedef uint32_t u_int32_t;
+typedef uint16_t u_int16_t;
+typedef uint8_t u_int8_t;
+#endif
+
typedef u_int64_t u64;
typedef u_int32_t u32;
typedef u_int16_t u16;
typedef u_int8_t u8;
typedef u32 block_t;
typedef u32 nid_t;
+#ifndef bool
typedef u8 bool;
+#endif
typedef unsigned long pgoff_t;
typedef unsigned short umode_t;
+#ifndef HAVE_LINUX_TYPES_H
+typedef u8 __u8;
+typedef u16 __u16;
+typedef u32 __u32;
+typedef u64 __u64;
+typedef u16 __le16;
+typedef u32 __le32;
+typedef u64 __le64;
+typedef u16 __be16;
+typedef u32 __be32;
+typedef u64 __be64;
+#endif
+
#if HAVE_BYTESWAP_H
#include <byteswap.h>
#else
@@ -167,6 +219,14 @@ static inline uint64_t bswap_64(uint64_t val)
printf("%-30s" fmt, #member, ((ptr)->member)); \
} while (0)
+#define DISP_u16(ptr, member) \
+ do { \
+ assert(sizeof((ptr)->member) == 2); \
+ printf("%-30s" "\t\t[0x%8x : %u]\n", \
+ #member, le16_to_cpu(((ptr)->member)), \
+ le16_to_cpu(((ptr)->member))); \
+ } while (0)
+
#define DISP_u32(ptr, member) \
do { \
assert(sizeof((ptr)->member) <= 4); \
@@ -209,7 +269,9 @@ static inline uint64_t bswap_64(uint64_t val)
snprintf(buf, len, #member)
/* these are defined in kernel */
+#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
+#endif
#define PAGE_CACHE_SIZE 4096
#define BITS_PER_BYTE 8
#define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */
@@ -230,6 +292,7 @@ static inline uint64_t bswap_64(uint64_t val)
#define VERSION_LEN 256
enum f2fs_config_func {
+ MKFS,
FSCK,
DUMP,
DEFRAG,
@@ -292,11 +355,13 @@ struct f2fs_configuration {
int trimmed;
int func;
void *private;
+ int dry_run;
int fix_on;
int bug_on;
int auto_fix;
int preen_mode;
int ro;
+ int preserve_limits; /* preserve quota limits */
__le32 feature; /* defined features */
/* defragmentation parameters */
@@ -308,6 +373,16 @@ struct f2fs_configuration {
/* sload parameters */
char *from_dir;
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
+
+ /* precomputed fs UUID checksum for seeding other checksums */
+ u_int32_t chksum_seed;
};
#ifdef CONFIG_64BIT
@@ -393,6 +468,7 @@ struct f2fs_configuration {
*/
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
#define round_down(x, y) ((x) & ~__round_mask(x, y))
+
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
@@ -447,6 +523,12 @@ enum {
#define F2FS_NODE_INO(sbi) (sbi->node_ino_num)
#define F2FS_META_INO(sbi) (sbi->meta_ino_num)
+#define F2FS_MAX_QUOTAS 3
+#define QUOTA_DATA(i) (2)
+#define QUOTA_INO(sb,t) (le32_to_cpu((sb)->qf_ino[t]))
+
+#define FS_IMMUTABLE_FL 0x00000010 /* Immutable file */
+
/* This flag is used by node and meta inodes, and by recovery */
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
@@ -460,19 +542,31 @@ enum {
#define MAX_ACTIVE_NODE_LOGS 8
#define MAX_ACTIVE_DATA_LOGS 8
-#define F2FS_FEATURE_ENCRYPT 0x0001
-#define F2FS_FEATURE_BLKZONED 0x0002
+#define F2FS_FEATURE_ENCRYPT 0x0001
+#define F2FS_FEATURE_BLKZONED 0x0002
+#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
+#define F2FS_FEATURE_EXTRA_ATTR 0x0008
+#define F2FS_FEATURE_PRJQUOTA 0x0010
+#define F2FS_FEATURE_INODE_CHKSUM 0x0020
+#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040
+#define F2FS_FEATURE_QUOTA_INO 0x0080
#define MAX_VOLUME_NAME 512
/*
* For superblock
*/
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_device {
__u8 path[MAX_PATH_LEN];
__le32 total_segments;
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_super_block {
__le32 magic; /* Magic Number */
__le16 major_ver; /* Major Version */
@@ -512,7 +606,8 @@ struct f2fs_super_block {
__u8 encryption_level; /* versioning level for encryption */
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
struct f2fs_device devs[MAX_DEVICES]; /* device list */
- __u8 reserved[327]; /* valid reserved region */
+ __le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */
+ __u8 reserved[315]; /* valid reserved region */
} __attribute__((packed));
/*
@@ -528,6 +623,9 @@ struct f2fs_super_block {
#define CP_ORPHAN_PRESENT_FLAG 0x00000002
#define CP_UMOUNT_FLAG 0x00000001
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_checkpoint {
__le64 checkpoint_ver; /* checkpoint block version number */
__le64 user_block_count; /* # of user blocks */
@@ -564,6 +662,9 @@ struct f2fs_checkpoint {
*/
#define F2FS_ORPHANS_PER_BLOCK 1020
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_orphan_block {
__le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */
__le32 reserved; /* reserved */
@@ -576,6 +677,9 @@ struct f2fs_orphan_block {
/*
* For NODE structure
*/
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_extent {
__le32 fofs; /* start file offset of the extent */
__le32 blk_addr; /* start block address of the extent */
@@ -583,11 +687,12 @@ struct f2fs_extent {
} __attribute__((packed));
#define F2FS_NAME_LEN 255
-#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
+/* 200 bytes for inline xattrs by default */
+#define DEFAULT_INLINE_XATTR_ADDRS 50
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
+#define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \
+ __get_extra_isize(inode))
#define ADDRS_PER_INODE(i) addrs_per_inode(i)
-#define DEF_ADDRS_PER_INODE_INLINE_XATTR \
- (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS)
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
@@ -602,12 +707,31 @@ struct f2fs_extent {
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
+#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */
-#define MAX_INLINE_DATA (sizeof(__le32) * \
- (DEF_ADDRS_PER_INODE_INLINE_XATTR - 1))
+#if !defined(offsetof)
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#define F2FS_TOTAL_EXTRA_ATTR_SIZE \
+ (offsetof(struct f2fs_inode, i_extra_end) - \
+ offsetof(struct f2fs_inode, i_extra_isize)) \
+
+#define F2FS_DEF_PROJID 0 /* default project ID */
+
+#define MAX_INLINE_DATA(node) (sizeof(__le32) * \
+ (DEF_ADDRS_PER_INODE - \
+ get_inline_xattr_addrs(&node->i) - \
+ get_extra_isize(node) - \
+ DEF_INLINE_RESERVED_SIZE))
+#define DEF_MAX_INLINE_DATA (sizeof(__le32) * \
+ (DEF_ADDRS_PER_INODE - \
+ DEFAULT_INLINE_XATTR_ADDRS - \
+ F2FS_TOTAL_EXTRA_ATTR_SIZE - \
+ DEF_INLINE_RESERVED_SIZE))
#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \
- - sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1))
+ - sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - \
+ DEF_INLINE_RESERVED_SIZE))
#define DEF_DIR_LEVEL 0
@@ -622,6 +746,9 @@ struct f2fs_extent {
#define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
#define file_enc_name(fi) ((fi)->i_advise & FADVISE_ENC_NAME_BIT)
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_inode {
__le16 i_mode; /* file mode */
__u8 i_advise; /* file hints */
@@ -648,16 +775,31 @@ struct f2fs_inode {
struct f2fs_extent i_ext; /* caching a largest extent */
- __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
-
+ union {
+ struct {
+ __le16 i_extra_isize; /* extra inode attribute size */
+ __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */
+ __le32 i_projid; /* project id */
+ __le32 i_inode_checksum;/* inode meta checksum */
+ __le32 i_extra_end[0]; /* for attribute size calculation */
+ };
+ __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
+ };
__le32 i_nid[5]; /* direct(2), indirect(2),
double_indirect(1) node id */
} __attribute__((packed));
+
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct direct_node {
__le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct indirect_node {
__le32 nid[NIDS_PER_BLOCK]; /* array of data block address */
} __attribute__((packed));
@@ -671,7 +813,9 @@ enum {
#define XATTR_NODE_OFFSET ((((unsigned int)-1) << OFFSET_BIT_SHIFT) \
>> OFFSET_BIT_SHIFT)
-
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct node_footer {
__le32 nid; /* node id */
__le32 ino; /* inode nunmber */
@@ -680,6 +824,9 @@ struct node_footer {
__le32 next_blkaddr; /* next node page block address */
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_node {
/* can be one of three types: inode, direct, and indirect types */
union {
@@ -696,12 +843,18 @@ struct f2fs_node {
#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry))
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_nat_entry {
__u8 version; /* latest version of cached nat entry */
__le32 ino; /* inode number */
__le32 block_addr; /* block address */
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_nat_block {
struct f2fs_nat_entry entries[NAT_ENTRY_PER_BLOCK];
} __attribute__((packed));
@@ -721,7 +874,7 @@ struct f2fs_nat_block {
* disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments.
*/
#define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2)
-#define MAX_SIT_BITMAP_SIZE (SEG_ALIGN(ALIGN(F2FS_MAX_SEGMENT, \
+#define MAX_SIT_BITMAP_SIZE (SEG_ALIGN(SIZE_ALIGN(F2FS_MAX_SEGMENT, \
SIT_ENTRY_PER_BLOCK)) * \
c.blks_per_seg / 8)
@@ -738,12 +891,18 @@ struct f2fs_nat_block {
((le16_to_cpu((raw_sit)->vblocks) & ~SIT_VBLOCKS_MASK) \
>> SIT_VBLOCKS_SHIFT)
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_sit_entry {
__le16 vblocks; /* reference above */
__u8 valid_map[SIT_VBLOCK_MAP_SIZE]; /* bitmap for valid blocks */
__le64 mtime; /* segment age for cleaning */
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_sit_block {
struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK];
} __attribute__((packed));
@@ -769,10 +928,16 @@ struct f2fs_sit_block {
#define SUM_ENTRIES_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM)
/* a summary entry for a 4KB-sized block in a segment */
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_summary {
__le32 nid; /* parent node id */
union {
__u8 reserved[3];
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct {
__u8 version; /* node version number */
__le16 ofs_in_node; /* block index in parent node */
@@ -784,6 +949,9 @@ struct f2fs_summary {
#define SUM_TYPE_NODE (1)
#define SUM_TYPE_DATA (0)
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct summary_footer {
unsigned char entry_type; /* SUM_TYPE_XXX */
__le32 check_sum; /* summary checksum */
@@ -815,31 +983,49 @@ enum {
SIT_JOURNAL
};
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct nat_journal_entry {
__le32 nid;
struct f2fs_nat_entry ne;
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct nat_journal {
struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES];
__u8 reserved[NAT_JOURNAL_RESERVED];
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct sit_journal_entry {
__le32 segno;
struct f2fs_sit_entry se;
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct sit_journal {
struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES];
__u8 reserved[SIT_JOURNAL_RESERVED];
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_extra_info {
__le64 kbytes_written;
__u8 reserved[EXTRA_INFO_RESERVED];
} __attribute__((packed));
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_journal {
union {
__le16 n_nats;
@@ -854,6 +1040,9 @@ struct f2fs_journal {
} __attribute__((packed));
/* 4KB-sized summary block structure */
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_summary_block {
struct f2fs_summary entries[ENTRIES_IN_SUM];
struct f2fs_journal journal;
@@ -893,6 +1082,9 @@ typedef __le32 f2fs_hash_t;
NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP))
/* One directory entry slot representing F2FS_SLOT_LEN-sized file name */
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_dir_entry {
__le32 hash_code; /* hash code of file name */
__le32 ino; /* inode number */
@@ -901,6 +1093,9 @@ struct f2fs_dir_entry {
} __attribute__((packed));
/* 4KB-sized directory entry block */
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
struct f2fs_dentry_block {
/* validity bitmap for directory entries in each block */
__u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP];
@@ -909,23 +1104,19 @@ struct f2fs_dentry_block {
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
} __attribute__((packed));
+/* for inline stuff */
+#define DEF_INLINE_RESERVED_SIZE 1
+
/* for inline dir */
-#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \
+#define NR_INLINE_DENTRY(node) (MAX_INLINE_DATA(node) * BITS_PER_BYTE / \
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
BITS_PER_BYTE + 1))
-#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \
+#define INLINE_DENTRY_BITMAP_SIZE(node) ((NR_INLINE_DENTRY(node) + \
BITS_PER_BYTE - 1) / BITS_PER_BYTE)
-#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \
+#define INLINE_RESERVED_SIZE(node) (MAX_INLINE_DATA(node) - \
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
- NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE))
-
-/* inline directory entry structure */
-struct f2fs_inline_dentry {
- __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE];
- __u8 reserved[INLINE_RESERVED_SIZE];
- struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
- __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
-} __attribute__((packed));
+ NR_INLINE_DENTRY(node) + \
+ INLINE_DENTRY_BITMAP_SIZE(node)))
/* file types used in inode_info->flags */
enum FILE_TYPE {
@@ -954,6 +1145,7 @@ extern int utf8_to_utf16(u_int16_t *, const char *, size_t, size_t);
extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t);
extern int log_base_2(u_int32_t);
extern unsigned int addrs_per_inode(struct f2fs_inode *);
+extern __u32 f2fs_inode_chksum(struct f2fs_node *);
extern int get_bits_in_byte(unsigned char n);
extern int test_and_set_bit_le(u32, u8 *);
@@ -973,7 +1165,9 @@ extern int f2fs_devs_are_umounted(void);
extern int f2fs_dev_is_umounted(char *);
extern int f2fs_get_device_info(void);
extern int get_device_info(int);
-extern void f2fs_finalize_device(void);
+extern int f2fs_init_sparse_file(void);
+extern int f2fs_finalize_device(void);
+extern int f2fs_fsync_device(void);
extern int dev_read(void *, __u64, size_t);
extern int dev_write(void *, __u64, size_t);
@@ -990,6 +1184,32 @@ extern int dev_read_version(void *, __u64, size_t);
extern void get_kernel_version(__u8 *);
f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int);
+static inline bool f2fs_has_extra_isize(struct f2fs_inode *inode)
+{
+ return (inode->i_inline & F2FS_EXTRA_ATTR);
+}
+
+static inline int __get_extra_isize(struct f2fs_inode *inode)
+{
+ if (f2fs_has_extra_isize(inode))
+ return le16_to_cpu(inode->i_extra_isize) / sizeof(__le32);
+ return 0;
+}
+
+extern struct f2fs_configuration c;
+static inline int get_inline_xattr_addrs(struct f2fs_inode *inode)
+{
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR))
+ return le16_to_cpu(inode->i_inline_xattr_size);
+ else if (inode->i_inline & F2FS_INLINE_XATTR ||
+ inode->i_inline & F2FS_INLINE_DENTRY)
+ return DEFAULT_INLINE_XATTR_ADDRS;
+ else
+ return 0;
+}
+
+#define get_extra_isize(node) __get_extra_isize(&node->i)
+
#define F2FS_ZONED_NONE 0
#define F2FS_ZONED_HA 1
#define F2FS_ZONED_HM 2
@@ -1059,9 +1279,9 @@ extern int f2fs_reset_zones(int);
extern struct f2fs_configuration c;
-#define ALIGN(val, size) ((val) + (size) - 1) / (size)
-#define SEG_ALIGN(blks) ALIGN(blks, c.blks_per_seg)
-#define ZONE_ALIGN(blks) ALIGN(blks, c.blks_per_seg * \
+#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)
static inline double get_best_overprovision(struct f2fs_super_block *sb)
@@ -1103,4 +1323,24 @@ static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp)
return cpu_to_le64(cp_ver);
}
+static inline int exist_qf_ino(struct f2fs_super_block *sb)
+{
+ int i;
+
+ for (i = 0; i < F2FS_MAX_QUOTAS; i++)
+ if (sb->qf_ino[i])
+ return 1;
+ return 0;
+}
+
+static inline int is_qf_ino(struct f2fs_super_block *sb, nid_t ino)
+{
+ int i;
+
+ for (i = 0; i < F2FS_MAX_QUOTAS; i++)
+ if (sb->qf_ino[i] == ino)
+ return 1;
+ return 0;
+}
+
#endif /*__F2FS_FS_H */
diff --git a/include/list.h b/include/list.h
deleted file mode 100644
index 571cd5c..0000000
--- a/include/list.h
+++ /dev/null
@@ -1,88 +0,0 @@
-
-#define POISON_POINTER_DELTA 0
-#define LIST_POISON1 ((void *) (0x00100100 + POISON_POINTER_DELTA))
-#define LIST_POISON2 ((void *) (0x00200200 + POISON_POINTER_DELTA))
-
-#if !defined(offsetof)
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-static inline void INIT_LIST_HEAD(struct list_head *list)
-{
- list->next = list;
- list->prev = list;
-}
-
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-static inline void __list_del_entry(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
-}
-
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
-}
-
-static inline int list_empty(const struct list_head *head)
-{
- return head->next == head;
-}
-
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
-
-#define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-#define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-#define list_for_each_entry_safe(pos, n, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member), \
- n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
diff --git a/include/quota.h b/include/quota.h
new file mode 100644
index 0000000..cfb9861
--- /dev/null
+++ b/include/quota.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * Header file for disk format of new quotafile format
+ *
+ * Copied essential definitions and structures for mkfs.f2fs from quotaio.h
+ *
+ * Aditya Kali <adityakali@google.com>
+ * Jan Kara <jack@suse.cz>
+ * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
+ *
+ */
+#ifndef F2FS_QUOTA_H
+#define F2FS_QUOTA_H
+
+enum quota_type {
+ USRQUOTA = 0,
+ GRPQUOTA = 1,
+ PRJQUOTA = 2,
+ MAXQUOTAS = 3,
+};
+
+#if MAXQUOTAS > 32
+#error "cannot have more than 32 quota types to fit in qtype_bits"
+#endif
+
+#define QUOTA_USR_BIT (1 << USRQUOTA)
+#define QUOTA_GRP_BIT (1 << GRPQUOTA)
+#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
+#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927, /* GRPQUOTA */\
+ 0xd9c03f14 /* PRJQUOTA */\
+}
+
+#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */
+
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+
+#define QT_TREEOFF 1 /* Offset of tree in file in blocks */
+
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
+struct v2_disk_dqheader {
+ u_int32_t dqh_magic; /* Magic number identifying file */
+ u_int32_t dqh_version; /* File version */
+} __attribute__ ((packed));
+
+/* Header with type and version specific information */
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
+struct v2_disk_dqinfo {
+ u_int32_t dqi_bgrace; /* Time before block soft limit becomes hard limit */
+ u_int32_t dqi_igrace; /* Time before inode soft limit becomes hard limit */
+ u_int32_t dqi_flags; /* Flags for quotafile (DQF_*) */
+ u_int32_t dqi_blocks; /* Number of blocks in file */
+ u_int32_t dqi_free_blk; /* Number of first free block in the list */
+ u_int32_t dqi_free_entry; /* Number of block with at least one free entry */
+} __attribute__ ((packed));
+
+#ifdef ANDROID_WINDOWS_HOST
+#pragma pack(1)
+#endif
+struct v2r1_disk_dqblk {
+ __le32 dqb_id; /* id this quota applies to */
+ __le32 dqb_pad;
+ __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __le64 dqb_isoftlimit; /* preferred inode limit */
+ __le64 dqb_curinodes; /* current # allocated inodes */
+ __le64 dqb_bhardlimit; /* absolute limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_bsoftlimit; /* preferred limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_curspace; /* current space occupied (in bytes) */
+ __le64 dqb_btime; /* time limit for excessive disk use */
+ __le64 dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+#endif
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 31836db..ffdbccb 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -8,32 +8,47 @@
*/
#define _LARGEFILE64_SOURCE
+#include <f2fs_fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
#include <mntent.h>
+#endif
#include <time.h>
#include <sys/stat.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/mount.h>
#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
#ifndef WITH_ANDROID
+#ifdef HAVE_SCSI_SG_H
#include <scsi/sg.h>
#endif
+#endif
+#ifdef HAVE_LINUX_HDREG_H
#include <linux/hdreg.h>
+#endif
+#ifdef HAVE_LINUX_LIMITS_H
#include <linux/limits.h>
-
-#include <f2fs_fs.h>
+#endif
#ifndef WITH_ANDROID
/* SCSI command for standard inquiry*/
#define MODELINQUIRY 0x12,0x00,0x00,0x00,0x4A,0x00
#endif
-#ifndef _WIN32 /* O_BINARY is windows-specific flag */
+#ifndef ANDROID_WINDOWS_HOST /* O_BINARY is windows-specific flag */
#define O_BINARY 0
+#else
+/* On Windows, wchar_t is 8 bit sized and it causes compilation errors. */
+#define wchar_t int
#endif
/*
@@ -456,9 +471,7 @@ f2fs_hash_t f2fs_dentry_hash(const unsigned char *name, int len)
unsigned int addrs_per_inode(struct f2fs_inode *i)
{
- if (i->i_inline & F2FS_INLINE_XATTR)
- return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS;
- return DEF_ADDRS_PER_INODE;
+ return CUR_ADDRS_PER_INODE(i) - get_inline_xattr_addrs(i);
}
/*
@@ -493,11 +506,36 @@ int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len)
return 0;
}
+__u32 f2fs_inode_chksum(struct f2fs_node *node)
+{
+ struct f2fs_inode *ri = &node->i;
+ __le32 ino = node->footer.ino;
+ __le32 gen = ri->i_generation;
+ __u32 chksum, chksum_seed;
+ __u32 dummy_cs = 0;
+ unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum);
+ unsigned int cs_size = sizeof(dummy_cs);
+
+ chksum = f2fs_cal_crc32(c.chksum_seed, (__u8 *)&ino,
+ sizeof(ino));
+ chksum_seed = f2fs_cal_crc32(chksum, (__u8 *)&gen, sizeof(gen));
+
+ chksum = f2fs_cal_crc32(chksum_seed, (__u8 *)ri, offset);
+ chksum = f2fs_cal_crc32(chksum, (__u8 *)&dummy_cs, cs_size);
+ offset += cs_size;
+ chksum = f2fs_cal_crc32(chksum, (__u8 *)ri + offset,
+ F2FS_BLKSIZE - offset);
+ return chksum;
+}
+
/*
* try to identify the root device
*/
const char *get_rootdev()
{
+#ifdef ANDROID_WINDOWS_HOST
+ return NULL;
+#else
struct stat sb;
int fd, ret;
char buf[32];
@@ -538,6 +576,7 @@ const char *get_rootdev()
snprintf(rootdev, PATH_MAX + 1, "/dev/%s", buf);
return rootdev;
+#endif
}
/*
@@ -557,6 +596,11 @@ void f2fs_init_configuration(void)
c.zoned_mode = 0;
c.zoned_model = 0;
c.zone_blocks = 0;
+#ifdef WITH_ANDROID
+ c.preserve_limits = 0;
+#else
+ c.preserve_limits = 1;
+#endif
for (i = 0; i < MAX_DEVICES; i++) {
memset(&c.devices[i], 0, sizeof(struct device_info));
@@ -578,8 +622,11 @@ void f2fs_init_configuration(void)
c.trimmed = 0;
c.ro = 0;
c.kd = -1;
+ c.dry_run = 0;
+ c.fixed_time = -1;
}
+#ifdef HAVE_SETMNTENT
static int is_mounted(const char *mpt, const char *device)
{
FILE *file = NULL;
@@ -601,9 +648,13 @@ static int is_mounted(const char *mpt, const char *device)
endmntent(file);
return mnt ? 1 : 0;
}
+#endif
int f2fs_dev_is_umounted(char *path)
{
+#ifdef ANDROID_WINDOWS_HOST
+ return 0;
+#else
struct stat st_buf;
int is_rootdev = 0;
int ret = 0;
@@ -615,29 +666,36 @@ int f2fs_dev_is_umounted(char *path)
* try with /proc/mounts fist to detect RDONLY.
* f2fs_stop_checkpoint makes RO in /proc/mounts while RW in /etc/mtab.
*/
+#ifdef __linux__
ret = is_mounted("/proc/mounts", path);
if (ret) {
MSG(0, "Info: Mounted device!\n");
return -1;
}
-
+#endif
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif
ret = is_mounted(MOUNTED, path);
if (ret) {
MSG(0, "Info: Mounted device!\n");
return -1;
}
-
+#endif
/*
* If we are supposed to operate on the root device, then
* also check the mounts for '/dev/root', which sometimes
* functions as an alias for the root device.
*/
if (is_rootdev) {
+#ifdef __linux__
ret = is_mounted("/proc/mounts", "/dev/root");
if (ret) {
MSG(0, "Info: Mounted device!\n");
return -1;
}
+#endif
}
/*
@@ -654,7 +712,8 @@ int f2fs_dev_is_umounted(char *path)
return -1;
}
}
- return 0;
+ return ret;
+#endif
}
int f2fs_devs_are_umounted(void)
@@ -677,6 +736,26 @@ void get_kernel_version(__u8 *version)
memset(version + i, 0, VERSION_LEN + 1 - i);
}
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96)
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114, size_t)
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
+#define BLKSSZGET _IO(0x12,104)
+#endif
+
+#if defined(__APPLE__)
+#include <sys/disk.h>
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT
+#define BLKSSZGET DKIOCGETBLOCKCOUNT
+#endif /* APPLE_DARWIN */
+
+#ifndef ANDROID_WINDOWS_HOST
int get_device_info(int i)
{
int32_t fd = 0;
@@ -685,8 +764,10 @@ int get_device_info(int i)
uint32_t total_sectors;
#endif
struct stat stat_buf;
+#ifdef HDIO_GETGIO
struct hd_geometry geom;
-#ifndef WITH_ANDROID
+#endif
+#if !defined(WITH_ANDROID) && defined(__linux__)
sg_io_hdr_t io_hdr;
unsigned char reply_buffer[96] = {0};
unsigned char model_inq[6] = {MODELINQUIRY};
@@ -694,7 +775,7 @@ int get_device_info(int i)
struct device_info *dev = c.devices + i;
if (c.sparse_mode) {
- fd = open((char *)dev->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ fd = open((char *)dev->path, O_RDWR | O_CREAT | O_BINARY, 0644);
} else {
fd = open((char *)dev->path, O_RDWR);
}
@@ -705,6 +786,11 @@ int get_device_info(int i)
dev->fd = fd;
+ if (c.sparse_mode) {
+ if (f2fs_init_sparse_file())
+ return -1;
+ }
+
if (c.kd == -1) {
c.kd = open("/proc/version", O_RDONLY);
if (c.kd < 0) {
@@ -723,10 +809,12 @@ int get_device_info(int i)
} else if (S_ISREG(stat_buf.st_mode)) {
dev->total_sectors = stat_buf.st_size / dev->sector_size;
} else if (S_ISBLK(stat_buf.st_mode)) {
+#ifdef BLKSSZGET
if (ioctl(fd, BLKSSZGET, &sector_size) < 0)
MSG(0, "\tError: Using the default sector size\n");
else if (dev->sector_size < sector_size)
dev->sector_size = sector_size;
+#endif
#ifdef BLKGETSIZE64
if (ioctl(fd, BLKGETSIZE64, &dev->total_sectors) < 0) {
MSG(0, "\tError: Cannot get the device size\n");
@@ -742,13 +830,17 @@ int get_device_info(int i)
dev->total_sectors /= dev->sector_size;
if (i == 0) {
+#ifdef HDIO_GETGIO
if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
c.start_sector = 0;
else
c.start_sector = geom.start;
+#else
+ c.start_sector = 0;
+#endif
}
-#ifndef WITH_ANDROID
+#if !defined(WITH_ANDROID) && defined(__linux__)
/* Send INQUIRY command */
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
@@ -782,7 +874,7 @@ int get_device_info(int i)
return -1;
}
-#ifndef WITH_ANDROID
+#if !defined(WITH_ANDROID) && defined(__linux__)
if (S_ISBLK(stat_buf.st_mode))
f2fs_get_zoned_model(i);
@@ -812,6 +904,102 @@ int get_device_info(int i)
return 0;
}
+#else
+
+#include "windows.h"
+#include "winioctl.h"
+
+#if (_WIN32_WINNT >= 0x0500)
+#define HAVE_GET_FILE_SIZE_EX 1
+#endif
+
+static int win_get_device_size(const char *file, uint64_t *device_size)
+{
+ HANDLE dev;
+ PARTITION_INFORMATION pi;
+ DISK_GEOMETRY gi;
+ DWORD retbytes;
+#ifdef HAVE_GET_FILE_SIZE_EX
+ LARGE_INTEGER filesize;
+#else
+ DWORD filesize;
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+ dev = CreateFile(file, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (dev == INVALID_HANDLE_VALUE)
+ return EBADF;
+ if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
+ &pi, sizeof(PARTITION_INFORMATION),
+ &pi, sizeof(PARTITION_INFORMATION),
+ &retbytes, NULL)) {
+
+ *device_size = pi.PartitionLength.QuadPart;
+
+ } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ &gi, sizeof(DISK_GEOMETRY),
+ &gi, sizeof(DISK_GEOMETRY),
+ &retbytes, NULL)) {
+
+ *device_size = gi.BytesPerSector *
+ gi.SectorsPerTrack *
+ gi.TracksPerCylinder *
+ gi.Cylinders.QuadPart;
+
+#ifdef HAVE_GET_FILE_SIZE_EX
+ } else if (GetFileSizeEx(dev, &filesize)) {
+ *device_size = filesize.QuadPart;
+ }
+#else
+ } else {
+ filesize = GetFileSize(dev, NULL);
+ if (INVALID_FILE_SIZE != filesize)
+ return -1;
+ *device_size = filesize;
+ }
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+ CloseHandle(dev);
+ return 0;
+}
+
+int get_device_info(int i)
+{
+ struct device_info *dev = c.devices + i;
+ uint64_t device_size = 0;
+ int32_t fd = 0;
+
+ /* Block device target is not supported on Windows. */
+ if (!c.sparse_mode) {
+ if (win_get_device_size(dev->path, &device_size)) {
+ MSG(0, "\tError: Failed to get device size!\n");
+ return -1;
+ }
+ } else {
+ device_size = c.device_size;
+ }
+ if (c.sparse_mode) {
+ fd = open((char *)dev->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ } else {
+ fd = open((char *)dev->path, O_RDWR | O_BINARY);
+ }
+ if (fd < 0) {
+ MSG(0, "\tError: Failed to open the device!\n");
+ return -1;
+ }
+ dev->fd = fd;
+ dev->total_sectors = device_size / dev->sector_size;
+ c.start_sector = 0;
+ c.sector_size = dev->sector_size;
+ c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size;
+ c.total_sectors += dev->total_sectors;
+
+ return 0;
+}
+#endif
+
int f2fs_get_device_info(void)
{
int i;
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index aa99068..4781517 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -14,12 +14,18 @@
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
#include <mntent.h>
+#endif
#include <time.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_HDREG_H
#include <linux/hdreg.h>
+#endif
#include <f2fs_fs.h>
@@ -28,14 +34,8 @@ struct f2fs_configuration c;
#ifdef WITH_ANDROID
#include <sparse/sparse.h>
struct sparse_file *f2fs_sparse_file;
-
-struct buf_item {
- void *buf;
- size_t len;
- struct buf_item *next;
-};
-
-struct buf_item *buf_list;
+static char **blocks;
+u_int64_t blocks_count;
#endif
static int __get_device_fd(__u64 *offset)
@@ -54,6 +54,15 @@ static int __get_device_fd(__u64 *offset)
return -1;
}
+#ifndef HAVE_LSEEK64
+typedef off_t off64_t;
+
+static inline off64_t lseek64(int fd, __u64 offset, int set)
+{
+ return lseek(fd, offset, set);
+}
+#endif
+
/*
* IO interfaces
*/
@@ -68,12 +77,89 @@ int dev_read_version(void *buf, __u64 offset, size_t len)
return 0;
}
+#ifdef WITH_ANDROID
+static int sparse_read_blk(__u64 block, int count, void *buf)
+{
+ int i;
+ char *out = buf;
+ __u64 cur_block;
+
+ for (i = 0; i < count; ++i) {
+ cur_block = block + i;
+ if (blocks[cur_block])
+ memcpy(out + (i * F2FS_BLKSIZE),
+ blocks[cur_block], F2FS_BLKSIZE);
+ else if (blocks)
+ memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE);
+ }
+ return 0;
+}
+
+static int sparse_write_blk(__u64 block, int count, const void *buf)
+{
+ int i;
+ __u64 cur_block;
+ const char *in = buf;
+
+ for (i = 0; i < count; ++i) {
+ cur_block = block + i;
+ if (!blocks[cur_block]) {
+ blocks[cur_block] = calloc(1, F2FS_BLKSIZE);
+ if (!blocks[cur_block])
+ return -ENOMEM;
+ }
+ memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE),
+ F2FS_BLKSIZE);
+ }
+ return 0;
+}
+
+static int sparse_import_segment(void *UNUSED(priv), const void *data, int len,
+ unsigned int block, unsigned int nr_blocks)
+{
+ /* Ignore chunk headers, only write the data */
+ if (!nr_blocks || len % F2FS_BLKSIZE)
+ return 0;
+
+ return sparse_write_blk(block, nr_blocks, data);
+}
+
+static int sparse_merge_blocks(uint64_t start, uint64_t num)
+{
+ char *buf;
+ uint64_t i;
+
+ buf = calloc(num, F2FS_BLKSIZE);
+ if (!buf) {
+ fprintf(stderr, "failed to alloc %llu\n",
+ (unsigned long long)num * F2FS_BLKSIZE);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE);
+ free(blocks[start + i]);
+ blocks[start + i] = NULL;
+ }
+
+ /* free_sparse_blocks will release this buf. */
+ blocks[start] = buf;
+
+ return sparse_file_add_data(f2fs_sparse_file, blocks[start],
+ F2FS_BLKSIZE * num, start);
+}
+#else
+static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; }
+static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; }
+#endif
+
int dev_read(void *buf, __u64 offset, size_t len)
{
int fd;
if (c.sparse_mode)
- return 0;
+ return sparse_read_blk(offset / F2FS_BLKSIZE,
+ len / F2FS_BLKSIZE, buf);
fd = __get_device_fd(&offset);
if (fd < 0)
@@ -86,7 +172,11 @@ int dev_read(void *buf, __u64 offset, size_t len)
return 0;
}
+#ifdef POSIX_FADV_WILLNEED
int dev_readahead(__u64 offset, size_t len)
+#else
+int dev_readahead(__u64 offset, size_t UNUSED(len))
+#endif
{
int fd = __get_device_fd(&offset);
@@ -99,38 +189,16 @@ int dev_readahead(__u64 offset, size_t len)
#endif
}
-#ifdef WITH_ANDROID
-static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len)
-{
- struct buf_item *bi = calloc(1, sizeof(struct buf_item));
-
- if (bi == NULL) {
- return -1;
- }
- bi->buf = malloc(byte_len);
- if (bi->buf == NULL) {
- free(bi);
- return -1;
- }
-
- bi->len = byte_len;
- memcpy(bi->buf, buf, byte_len);
- bi->next = buf_list;
- buf_list = bi;
-
- sparse_file_add_data(f2fs_sparse_file, bi->buf, byte_len, byte_offset/F2FS_BLKSIZE);
- return 0;
-}
-#else
-static int dev_write_sparse(void *buf, __u64 byte_offset, size_t byte_len) { return 0; }
-#endif
-
int dev_write(void *buf, __u64 offset, size_t len)
{
int fd;
+ if (c.dry_run)
+ return 0;
+
if (c.sparse_mode)
- return dev_write_sparse(buf, offset, len);
+ return sparse_write_blk(offset / F2FS_BLKSIZE,
+ len / F2FS_BLKSIZE, buf);
fd = __get_device_fd(&offset);
if (fd < 0)
@@ -193,34 +261,116 @@ int dev_reada_block(__u64 blk_addr)
return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
}
-void f2fs_finalize_device(void)
+int f2fs_fsync_device(void)
{
+#ifndef ANDROID_WINDOWS_HOST
int i;
+ for (i = 0; i < c.ndevs; i++) {
+ if (fsync(c.devices[i].fd) < 0) {
+ MSG(0, "\tError: Could not conduct fsync!!!\n");
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+int f2fs_init_sparse_file(void)
+{
+#ifdef WITH_ANDROID
+ if (c.func == MKFS) {
+ f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
+ } else {
+ f2fs_sparse_file = sparse_file_import(c.devices[0].fd,
+ true, false);
+ if (!f2fs_sparse_file)
+ return -1;
+
+ c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0);
+ c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
+ }
+
+ if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) {
+ MSG(0, "\tError: Corrupted sparse file\n");
+ return -1;
+ }
+ blocks_count = c.device_size / F2FS_BLKSIZE;
+ blocks = calloc(blocks_count, sizeof(char *));
+
+ return sparse_file_foreach_chunk(f2fs_sparse_file, true, false,
+ sparse_import_segment, NULL);
+#else
+ MSG(0, "\tError: Sparse mode is only supported for android\n");
+ return -1;
+#endif
+}
+
+int f2fs_finalize_device(void)
+{
+ int i;
+ int ret = 0;
+
#ifdef WITH_ANDROID
if (c.sparse_mode) {
- sparse_file_write(f2fs_sparse_file, c.devices[0].fd, /*gzip*/0, /*sparse*/1, /*crc*/0);
- sparse_file_destroy(f2fs_sparse_file);
- while (buf_list) {
- struct buf_item *bi = buf_list;
- buf_list = buf_list->next;
- free(bi->buf);
- free(bi);
+ int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0;
+ uint64_t j;
+
+ if (c.func != MKFS) {
+ sparse_file_destroy(f2fs_sparse_file);
+ ret = ftruncate(c.devices[0].fd, 0);
+ ASSERT(!ret);
+ lseek(c.devices[0].fd, 0, SEEK_SET);
+ f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE,
+ c.device_size);
}
+
+ for (j = 0; j < blocks_count; ++j) {
+ if (!blocks[j] && chunk_start != -1) {
+ ret = sparse_merge_blocks(chunk_start,
+ j - chunk_start);
+ chunk_start = -1;
+ } else if (blocks[j] && chunk_start == -1) {
+ chunk_start = j;
+ }
+ ASSERT(!ret);
+ }
+ if (chunk_start != -1) {
+ ret = sparse_merge_blocks(chunk_start,
+ blocks_count - chunk_start);
+ ASSERT(!ret);
+ }
+
+ sparse_file_write(f2fs_sparse_file, c.devices[0].fd,
+ /*gzip*/0, /*sparse*/1, /*crc*/0);
+
+ sparse_file_destroy(f2fs_sparse_file);
+ for (j = 0; j < blocks_count; j++)
+ free(blocks[j]);
+ free(blocks);
+ blocks = NULL;
f2fs_sparse_file = NULL;
}
#endif
-
/*
* We should call fsync() to flush out all the dirty pages
* in the block device page cache.
*/
for (i = 0; i < c.ndevs; i++) {
- if (fsync(c.devices[i].fd) < 0)
+#ifndef ANDROID_WINDOWS_HOST
+ ret = fsync(c.devices[i].fd);
+ if (ret < 0) {
MSG(0, "\tError: Could not conduct fsync!!!\n");
-
- if (close(c.devices[i].fd) < 0)
+ break;
+ }
+#endif
+ ret = close(c.devices[i].fd);
+ if (ret < 0) {
MSG(0, "\tError: Failed to close device file!!!\n");
+ break;
+ }
}
close(c.kd);
+
+ return ret;
}
diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c
index eebf030..6e32f32 100644
--- a/lib/libf2fs_zoned.c
+++ b/lib/libf2fs_zoned.c
@@ -15,7 +15,9 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/ioctl.h>
+#endif
#include <libgen.h>
#include <f2fs_fs.h>
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index 0ea8b49..bbb4917 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -3,7 +3,9 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} ${libblkid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall -DWITH_BLKDISCARD
sbin_PROGRAMS = mkfs.f2fs
-mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c f2fs_format_utils.h $(top_srcdir)/include/f2fs_fs.h
+noinst_HEADERS = f2fs_format_utils.h
+include_HEADERS = $(top_srcdir)/include/f2fs_fs.h
+mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c
mkfs_f2fs_LDADD = ${libuuid_LIBS} ${libblkid_LIBS} $(top_builddir)/lib/libf2fs.la
lib_LTLIBRARIES = libf2fs_format.la
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index ff1153a..583b17f 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -13,12 +13,15 @@
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/stat.h>
#include <sys/mount.h>
+#endif
#include <time.h>
#include <uuid/uuid.h>
#include "f2fs_fs.h"
+#include "quota.h"
#include "f2fs_format_utils.h"
extern struct f2fs_configuration c;
@@ -32,6 +35,8 @@ 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)
+static unsigned int quotatype_bits = 0;
+
const char *media_ext_lists[] = {
"jpg",
"gif",
@@ -59,6 +64,7 @@ const char *media_ext_lists[] = {
"jpeg",
"video",
"apk", /* for android system */
+ "so", /* for android system */
NULL
};
@@ -120,14 +126,18 @@ next:
static void verify_cur_segs(void)
{
int i, j;
+ int reorder = 0;
for (i = 0; i < NR_CURSEG_TYPE; i++) {
- for (j = 0; j < NR_CURSEG_TYPE; j++)
- if (c.cur_seg[i] == c.cur_seg[j])
+ for (j = i + 1; j < NR_CURSEG_TYPE; j++) {
+ if (c.cur_seg[i] == c.cur_seg[j]) {
+ reorder = 1;
break;
+ }
+ }
}
- if (i == NR_CURSEG_TYPE && j == NR_CURSEG_TYPE)
+ if (!reorder)
return;
c.cur_seg[0] = 0;
@@ -149,6 +159,8 @@ static int f2fs_prepare_super_block(void)
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 next_ino;
+ enum quota_type qtype;
int i;
set_sb(magic, F2FS_SUPER_MAGIC);
@@ -244,7 +256,7 @@ static int f2fs_prepare_super_block(void)
set_sb(sit_blkaddr, get_sb(segment0_blkaddr) +
get_sb(segment_count_ckpt) * c.blks_per_seg);
- blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
+ blocks_for_sit = SIZE_ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
sit_segments = SEG_ALIGN(blocks_for_sit);
@@ -257,7 +269,7 @@ static int f2fs_prepare_super_block(void)
(get_sb(segment_count_ckpt) +
get_sb(segment_count_sit))) * c.blks_per_seg;
- blocks_for_nat = ALIGN(total_valid_blks_available,
+ blocks_for_nat = SIZE_ALIGN(total_valid_blks_available,
NAT_ENTRY_PER_BLOCK);
set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
@@ -369,11 +381,30 @@ static int f2fs_prepare_super_block(void)
uuid_generate(sb->uuid);
+ /* precompute checksum seed for metadata */
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ c.chksum_seed = f2fs_cal_crc32(~0, sb->uuid, sizeof(sb->uuid));
+
utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
MAX_VOLUME_NAME, strlen(c.vol_label));
set_sb(node_ino, 1);
set_sb(meta_ino, 2);
set_sb(root_ino, 3);
+ next_ino = 4;
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
+ quotatype_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
+ quotatype_bits |= QUOTA_PRJ_BIT;
+ }
+
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (!((1 << qtype) & quotatype_bits))
+ continue;
+ sb->qf_ino[qtype] = cpu_to_le32(next_ino++);
+ MSG(0, "Info: add quota type = %u => %u\n",
+ qtype, next_ino - 1);
+ }
if (total_zones <= 6) {
MSG(1, "\tError: %d zones: Need more zones "
@@ -505,6 +536,9 @@ static int f2fs_write_check_point_pack(void)
char *cp_payload = NULL;
char *sum_compact, *sum_compact_p;
struct f2fs_summary *sum_entry;
+ enum quota_type qtype;
+ u_int32_t quota_inum, quota_dnum;
+ int off;
int ret = -1;
cp = calloc(F2FS_BLKSIZE, 1);
@@ -542,7 +576,7 @@ static int f2fs_write_check_point_pack(void)
}
/* 1. cp page 1 of checkpoint pack 1 */
- cp->checkpoint_ver = rand() | 0x1;
+ 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]);
set_cp(cur_node_segno[2], c.cur_seg[CURSEG_COLD_NODE]);
@@ -554,9 +588,16 @@ static int f2fs_write_check_point_pack(void)
set_cp(cur_data_segno[i], 0xffffffff);
}
- set_cp(cur_node_blkoff[0], 1);
- set_cp(cur_data_blkoff[0], 1);
- set_cp(valid_block_count, 2);
+ quota_inum = quota_dnum = 0;
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++)
+ if (sb->qf_ino[qtype]) {
+ quota_inum++;
+ quota_dnum += QUOTA_DATA(qtype);
+ }
+
+ set_cp(cur_node_blkoff[0], 1 + quota_inum);
+ set_cp(cur_data_blkoff[0], 1 + quota_dnum);
+ set_cp(valid_block_count, 2 + quota_inum + quota_dnum);
set_cp(rsvd_segment_count, c.reserved_segments);
set_cp(overprov_segment_count, (get_sb(segment_count_main) -
get_cp(rsvd_segment_count)) *
@@ -585,9 +626,9 @@ static int f2fs_write_check_point_pack(void)
set_cp(ckpt_flags, flags);
set_cp(cp_pack_start_sum, 1 + get_sb(cp_payload));
- set_cp(valid_node_count, 1);
- set_cp(valid_inode_count, 1);
- set_cp(next_free_nid, get_sb(root_ino) + 1);
+ set_cp(valid_node_count, 1 + quota_inum);
+ set_cp(valid_inode_count, 1 + quota_inum);
+ set_cp(next_free_nid, get_sb(root_ino) + 1 + quota_inum);
set_cp(sit_ver_bitmap_bytesize, ((get_sb(segment_count_sit) / 2) <<
get_sb(log_blocks_per_seg)) / 8);
@@ -645,7 +686,7 @@ static int f2fs_write_check_point_pack(void)
SET_SUM_TYPE((&sum->footer), SUM_TYPE_DATA);
journal = &sum->journal;
- journal->n_nats = cpu_to_le16(1);
+ journal->n_nats = cpu_to_le16(1 + quota_inum);
journal->nat_j.entries[0].nid = sb->root_ino;
journal->nat_j.entries[0].ne.version = 0;
journal->nat_j.entries[0].ne.ino = sb->root_ino;
@@ -653,6 +694,19 @@ static int f2fs_write_check_point_pack(void)
get_sb(main_blkaddr) +
get_cp(cur_node_segno[0]) * c.blks_per_seg);
+ for (qtype = 0, i = 1; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ journal->nat_j.entries[i].nid = sb->qf_ino[qtype];
+ journal->nat_j.entries[i].ne.version = 0;
+ journal->nat_j.entries[i].ne.ino = sb->qf_ino[qtype];
+ journal->nat_j.entries[i].ne.block_addr = cpu_to_le32(
+ get_sb(main_blkaddr) +
+ get_cp(cur_node_segno[0]) *
+ c.blks_per_seg + i);
+ i++;
+ }
+
memcpy(sum_compact_p, &journal->n_nats, SUM_JOURNAL_SIZE);
sum_compact_p += SUM_JOURNAL_SIZE;
@@ -661,8 +715,11 @@ static int f2fs_write_check_point_pack(void)
journal->n_sits = cpu_to_le16(6);
journal->sit_j.entries[0].segno = cp->cur_node_segno[0];
journal->sit_j.entries[0].se.vblocks =
- cpu_to_le16((CURSEG_HOT_NODE << 10) | 1);
+ cpu_to_le16((CURSEG_HOT_NODE << 10) |
+ (1 + quota_inum));
f2fs_set_bit(0, (char *)journal->sit_j.entries[0].se.valid_map);
+ for (i = 1; i <= quota_inum; i++)
+ 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));
@@ -673,8 +730,12 @@ static int f2fs_write_check_point_pack(void)
/* 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);
+ cpu_to_le16((CURSEG_HOT_DATA << 10) |
+ (1 + quota_dnum));
f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
+ for (i = 1; i <= quota_dnum; i++)
+ 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));
@@ -689,6 +750,20 @@ static int f2fs_write_check_point_pack(void)
sum_entry = (struct f2fs_summary *)sum_compact_p;
sum_entry->nid = sb->root_ino;
sum_entry->ofs_in_node = 0;
+
+ off = 1;
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ int j;
+
+ for (j = 0; j < QUOTA_DATA(qtype); j++) {
+ (sum_entry + off + j)->nid = sb->qf_ino[qtype];
+ (sum_entry + off + j)->ofs_in_node = j;
+ }
+ off += QUOTA_DATA(qtype);
+ }
+
/* warm data summary, nothing to do */
/* cold data summary, nothing to do */
@@ -706,6 +781,13 @@ static int f2fs_write_check_point_pack(void)
sum->entries[0].nid = sb->root_ino;
sum->entries[0].ofs_in_node = 0;
+ for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ sum->entries[1 + i].nid = sb->qf_ino[qtype];
+ sum->entries[1 + i].ofs_in_node = 0;
+ i++;
+ }
cp_seg_blk++;
DBG(1, "\tWriting Segment summary for HOT_NODE, at offset 0x%08"PRIx64"\n",
@@ -847,16 +929,24 @@ static int f2fs_write_super_block(void)
static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
{
u_int64_t next_blkaddr = 0;
- u_int64_t root_inode_pos = get_sb(main_blkaddr);
+ u64 end_blkaddr = (get_sb(segment_count_main) <<
+ get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr);
+ u_int64_t start_inode_pos = get_sb(main_blkaddr);
+ u_int64_t last_inode_pos;
+ enum quota_type qtype;
+ u_int32_t quota_inum = 0;
+
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++)
+ if (sb->qf_ino[qtype]) quota_inum++;
/* only root inode was written before truncating dnodes */
- root_inode_pos += c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg;
+ last_inode_pos = start_inode_pos +
+ c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + quota_inum;
if (c.zoned_mode)
return 0;
do {
- if (offset < get_sb(main_blkaddr) ||
- offset >= get_sb(main_blkaddr) + get_sb(block_count))
+ if (offset < get_sb(main_blkaddr) || offset >= end_blkaddr)
break;
if (dev_read_block(raw_node, offset)) {
@@ -874,7 +964,7 @@ static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset)
}
offset = next_blkaddr;
/* should avoid recursive chain due to stale data */
- if (offset == root_inode_pos)
+ if (offset >= start_inode_pos || offset <= last_inode_pos)
break;
} while (1);
@@ -923,14 +1013,27 @@ static int f2fs_write_root_inode(void)
raw_node->i.i_current_depth = cpu_to_le32(1);
raw_node->i.i_dir_level = DEF_DIR_LEVEL;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ raw_node->i.i_inline = F2FS_EXTRA_ATTR;
+ raw_node->i.i_extra_isize =
+ cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE);
+ }
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
+ raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
+
data_blk_nor = get_sb(main_blkaddr) +
c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg;
- raw_node->i.i_addr[0] = cpu_to_le32(data_blk_nor);
+ raw_node->i.i_addr[get_extra_isize(raw_node)] = cpu_to_le32(data_blk_nor);
raw_node->i.i_ext.fofs = 0;
raw_node->i.i_ext.blk_addr = 0;
raw_node->i.i_ext.len = 0;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ raw_node->i.i_inode_checksum =
+ cpu_to_le32(f2fs_inode_chksum(raw_node));
+
main_area_node_seg_blk_offset = get_sb(main_blkaddr);
main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] *
c.blks_per_seg;
@@ -956,6 +1059,170 @@ static int f2fs_write_root_inode(void)
return -1;
}
#endif
+ free(raw_node);
+ return 0;
+}
+
+static int f2fs_write_default_quota(int qtype, unsigned int blkaddr,
+ __le32 raw_id)
+{
+ char *filebuf = calloc(F2FS_BLKSIZE, 2);
+ int file_magics[] = INITQMAGICS;
+ struct v2_disk_dqheader ddqheader;
+ struct v2_disk_dqinfo ddqinfo;
+ struct v2r1_disk_dqblk dqblk;
+
+ if (filebuf == NULL) {
+ MSG(1, "\tError: Calloc Failed for filebuf!!!\n");
+ return -1;
+ }
+
+ /* Write basic quota header */
+ ddqheader.dqh_magic = cpu_to_le32(file_magics[qtype]);
+ /* only support QF_VFSV1 */
+ ddqheader.dqh_version = cpu_to_le32(1);
+
+ memcpy(filebuf, &ddqheader, sizeof(ddqheader));
+
+ /* Fill Initial quota file content */
+ ddqinfo.dqi_bgrace = cpu_to_le32(MAX_DQ_TIME);
+ ddqinfo.dqi_igrace = cpu_to_le32(MAX_IQ_TIME);
+ ddqinfo.dqi_flags = cpu_to_le32(0);
+ ddqinfo.dqi_blocks = cpu_to_le32(QT_TREEOFF + 5);
+ ddqinfo.dqi_free_blk = cpu_to_le32(0);
+ ddqinfo.dqi_free_entry = cpu_to_le32(5);
+
+ memcpy(filebuf + V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo));
+
+ filebuf[1024] = 2;
+ filebuf[2048] = 3;
+ filebuf[3072] = 4;
+ filebuf[4096] = 5;
+
+ filebuf[5120 + 8] = 1;
+
+ dqblk.dqb_id = raw_id;
+ dqblk.dqb_pad = cpu_to_le32(0);
+ dqblk.dqb_ihardlimit = cpu_to_le64(0);
+ dqblk.dqb_isoftlimit = cpu_to_le64(0);
+ dqblk.dqb_curinodes = cpu_to_le64(1);
+ dqblk.dqb_bhardlimit = cpu_to_le64(0);
+ dqblk.dqb_bsoftlimit = cpu_to_le64(0);
+ dqblk.dqb_curspace = cpu_to_le64(4096);
+ dqblk.dqb_btime = cpu_to_le64(0);
+ dqblk.dqb_itime = cpu_to_le64(0);
+
+ memcpy(filebuf + 5136, &dqblk, sizeof(struct v2r1_disk_dqblk));
+
+ /* Write two blocks */
+ if (dev_write_block(filebuf, blkaddr) ||
+ dev_write_block(filebuf + F2FS_BLKSIZE, blkaddr + 1)) {
+ MSG(1, "\tError: While writing the quota_blk to disk!!!\n");
+ free(filebuf);
+ return -1;
+ }
+ DBG(1, "\tWriting quota data, at offset %08x, %08x\n",
+ blkaddr, blkaddr + 1);
+ free(filebuf);
+ return 0;
+}
+
+static int f2fs_write_qf_inode(int qtype)
+{
+ struct f2fs_node *raw_node = NULL;
+ u_int64_t data_blk_nor;
+ u_int64_t main_area_node_seg_blk_offset = 0;
+ __le32 raw_id;
+ int i;
+
+ raw_node = calloc(F2FS_BLKSIZE, 1);
+ if (raw_node == NULL) {
+ MSG(1, "\tError: Calloc Failed for raw_node!!!\n");
+ return -1;
+ }
+
+ raw_node->footer.nid = sb->qf_ino[qtype];
+ raw_node->footer.ino = sb->qf_ino[qtype];
+ raw_node->footer.cp_ver = cpu_to_le64(1);
+ raw_node->footer.next_blkaddr = cpu_to_le32(
+ get_sb(main_blkaddr) +
+ c.cur_seg[CURSEG_HOT_NODE] *
+ c.blks_per_seg + 1 + qtype + 1);
+
+ raw_node->i.i_mode = cpu_to_le16(0x8180);
+ raw_node->i.i_links = cpu_to_le32(1);
+ raw_node->i.i_uid = cpu_to_le32(getuid());
+ raw_node->i.i_gid = cpu_to_le32(getgid());
+
+ 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_nsec = 0;
+ raw_node->i.i_ctime = cpu_to_le32(time(NULL));
+ raw_node->i.i_ctime_nsec = 0;
+ raw_node->i.i_mtime = cpu_to_le32(time(NULL));
+ raw_node->i.i_mtime_nsec = 0;
+ raw_node->i.i_generation = 0;
+ raw_node->i.i_xattr_nid = 0;
+ raw_node->i.i_flags = FS_IMMUTABLE_FL;
+ raw_node->i.i_current_depth = cpu_to_le32(1);
+ raw_node->i.i_dir_level = DEF_DIR_LEVEL;
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ raw_node->i.i_inline = F2FS_EXTRA_ATTR;
+ raw_node->i.i_extra_isize =
+ cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE);
+ }
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
+ raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
+
+ data_blk_nor = get_sb(main_blkaddr) +
+ c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1;
+
+ for (i = 0; i < qtype; i++)
+ if (sb->qf_ino[i])
+ data_blk_nor += QUOTA_DATA(i);
+ if (qtype == 0)
+ raw_id = raw_node->i.i_uid;
+ else if (qtype == 1)
+ raw_id = raw_node->i.i_gid;
+ else if (qtype == 2)
+ raw_id = raw_node->i.i_projid;
+ else
+ ASSERT(0);
+
+ /* write two blocks */
+ if (f2fs_write_default_quota(qtype, data_blk_nor, raw_id)) {
+ free(raw_node);
+ return -1;
+ }
+
+ for (i = 0; i < QUOTA_DATA(qtype); i++)
+ raw_node->i.i_addr[get_extra_isize(raw_node) + i] =
+ cpu_to_le32(data_blk_nor + i);
+ raw_node->i.i_ext.fofs = 0;
+ raw_node->i.i_ext.blk_addr = 0;
+ raw_node->i.i_ext.len = 0;
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))
+ raw_node->i.i_inode_checksum =
+ cpu_to_le32(f2fs_inode_chksum(raw_node));
+
+ main_area_node_seg_blk_offset = get_sb(main_blkaddr);
+ main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] *
+ c.blks_per_seg + qtype + 1;
+
+ DBG(1, "\tWriting quota inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n",
+ get_sb(main_blkaddr),
+ c.cur_seg[CURSEG_HOT_NODE],
+ c.blks_per_seg, main_area_node_seg_blk_offset);
+ if (dev_write_block(raw_node, main_area_node_seg_blk_offset)) {
+ MSG(1, "\tError: While writing the raw_node to disk!!!\n");
+ free(raw_node);
+ return -1;
+ }
free(raw_node);
return 0;
@@ -965,6 +1232,8 @@ static int f2fs_update_nat_root(void)
{
struct f2fs_nat_block *nat_blk = NULL;
u_int64_t nat_seg_blk_offset = 0;
+ enum quota_type qtype;
+ int i;
nat_blk = calloc(F2FS_BLKSIZE, 1);
if(nat_blk == NULL) {
@@ -972,6 +1241,18 @@ static int f2fs_update_nat_root(void)
return -1;
}
+ /* update quota */
+ for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ nat_blk->entries[sb->qf_ino[qtype]].block_addr =
+ cpu_to_le32(get_sb(main_blkaddr) +
+ c.cur_seg[CURSEG_HOT_NODE] *
+ c.blks_per_seg + i + 1);
+ nat_blk->entries[sb->qf_ino[qtype]].ino = sb->qf_ino[qtype];
+ i++;
+ }
+
/* update root */
nat_blk->entries[get_sb(root_ino)].block_addr = cpu_to_le32(
get_sb(main_blkaddr) +
@@ -1026,6 +1307,7 @@ static int f2fs_add_default_dentry_root(void)
/* bitmap for . and .. */
test_and_set_bit_le(0, dent_blk->dentry_bitmap);
test_and_set_bit_le(1, dent_blk->dentry_bitmap);
+
data_blk_offset = get_sb(main_blkaddr);
data_blk_offset += c.cur_seg[CURSEG_HOT_DATA] *
c.blks_per_seg;
@@ -1044,6 +1326,7 @@ static int f2fs_add_default_dentry_root(void)
static int f2fs_create_root_dir(void)
{
+ enum quota_type qtype;
int err = 0;
err = f2fs_write_root_inode();
@@ -1052,6 +1335,16 @@ static int f2fs_create_root_dir(void)
goto exit;
}
+ for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
+ if (sb->qf_ino[qtype] == 0)
+ continue;
+ err = f2fs_write_qf_inode(qtype);
+ if (err < 0) {
+ MSG(1, "\tError: Failed to write quota inode!!!\n");
+ goto exit;
+ }
+ }
+
err = f2fs_update_nat_root();
if (err < 0) {
MSG(1, "\tError: Failed to update NAT for root!!!\n");
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index 5525d1c..e41bcb1 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -14,7 +14,9 @@
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/mount.h>
+#endif
#include <time.h>
#include <uuid/uuid.h>
#include <errno.h>
@@ -80,6 +82,16 @@ static void parse_feature(const char *features)
features++;
if (!strcmp(features, "encrypt")) {
c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
+ } else if (!strcmp(features, "extra_attr")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR);
+ } else if (!strcmp(features, "project_quota")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA);
+ } else if (!strcmp(features, "inode_checksum")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM);
+ } else if (!strcmp(features, "flexible_inline_xattr")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR);
+ } else if (!strcmp(features, "quota")) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO);
} else {
MSG(0, "Error: Wrong features\n");
mkfs_usage();
@@ -159,6 +171,24 @@ static void f2fs_parse_options(int argc, char *argv[])
}
}
+ if (!(c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR))) {
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) {
+ MSG(0, "\tInfo: project quota feature should always been"
+ "enabled with extra attr feature\n");
+ exit(1);
+ }
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) {
+ MSG(0, "\tInfo: inode checksum feature should always been"
+ "enabled with extra attr feature\n");
+ exit(1);
+ }
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) {
+ MSG(0, "\tInfo: flexible inline xattr feature should always been"
+ "enabled with extra attr feature\n");
+ exit(1);
+ }
+ }
+
if (optind >= argc) {
MSG(0, "\tError: Device not specified\n");
mkfs_usage();
@@ -263,6 +293,8 @@ int main(int argc, char *argv[])
f2fs_show_info();
+ c.func = MKFS;
+
if (!force_overwrite && f2fs_check_overwrite()) {
MSG(0, "\tUse the -f option to force overwrite.\n");
return -1;
@@ -292,20 +324,15 @@ int main(int argc, char *argv[])
}
if (c.sparse_mode) {
-#ifndef WITH_ANDROID
- MSG(0, "\tError: Sparse mode is only supported for android\n");
- return -1;
-#else
- if (f2fs_sparse_file)
- sparse_file_destroy(f2fs_sparse_file);
- f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
-#endif
+ if (f2fs_init_sparse_file())
+ return -1;
}
if (f2fs_format_device() < 0)
return -1;
- f2fs_finalize_device();
+ if (f2fs_finalize_device() < 0)
+ return -1;
MSG(0, "Info: format successful\n");
diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c
index 558684d..e481a8f 100644
--- a/mkfs/f2fs_format_utils.c
+++ b/mkfs/f2fs_format_utils.c
@@ -6,20 +6,26 @@
*
* Dual licensed under the GPL or LGPL version 2 licenses.
*/
+#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
+#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
+#include <f2fs_fs.h>
+
#include <stdio.h>
#include <unistd.h>
+#ifndef ANDROID_WINDOWS_HOST
#include <sys/ioctl.h>
+#endif
#include <sys/stat.h>
#include <fcntl.h>
-#include "f2fs_fs.h"
-
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
@@ -36,6 +42,7 @@
static int trim_device(int i)
{
+#ifndef ANDROID_WINDOWS_HOST
unsigned long long range[2];
struct stat stat_buf;
struct device_info *dev = c.devices + i;
@@ -68,7 +75,7 @@ static int trim_device(int i)
MSG(0, "Info: This device doesn't support BLKSECDISCARD\n");
} else {
MSG(0, "Info: Secure Discarded %lu MB\n",
- stat_buf.st_size >> 20);
+ (unsigned long)stat_buf.st_size >> 20);
return 0;
}
#endif
@@ -80,6 +87,8 @@ static int trim_device(int i)
} else
return -1;
#endif
+
+#endif
return 0;
}
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 5a9303f..81cf89b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,10 +2,14 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
-sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs f2fscrypt
+sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs
f2fstat_SOURCES = f2fstat.c
fibmap_f2fs_SOURCES = fibmap.c
parse_f2fs_SOURCES = f2fs_io_parse.c
+
+if LINUX
+sbin_PROGRAMS += f2fscrypt
f2fscrypt_SOURCES = f2fscrypt.c sha512.c
f2fscrypt_LDFLAGS = -luuid
dist_man_MANS = f2fscrypt.8
+endif
diff --git a/tools/f2fscrypt.c b/tools/f2fscrypt.c
index 48ea5f6..81ef830 100644
--- a/tools/f2fscrypt.c
+++ b/tools/f2fscrypt.c
@@ -30,7 +30,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_MNTENT_H
#include <mntent.h>
+#endif
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -38,7 +40,9 @@
#include <termios.h>
#include <unistd.h>
#include <signal.h>
+#ifdef __KERNEL__
#include <linux/fs.h>
+#endif
#include <uuid/uuid.h>
#if !defined(HAVE_ADD_KEY) || !defined(HAVE_KEYCTL)
@@ -47,6 +51,7 @@
#ifdef HAVE_SYS_KEY_H
#include <sys/key.h>
#endif
+#include <f2fs_fs.h>
#define F2FS_MAX_KEY_SIZE 64
#define F2FS_MAX_PASSPHRASE_SIZE 1024
@@ -121,7 +126,7 @@ int options;
extern void f2fs_sha512(const unsigned char *in, unsigned long in_size,
unsigned char *out);
-#ifndef HAVE_KEYCTL
+#if !defined(HAVE_KEYCTL)
static long keyctl(int cmd, ...)
{
va_list va;
@@ -137,7 +142,7 @@ static long keyctl(int cmd, ...)
}
#endif
-#ifndef HAVE_ADD_KEY
+#if !defined(HAVE_ADD_KEY)
static key_serial_t add_key(const char *type, const char *description,
const void *payload, size_t plen,
key_serial_t keyring)
diff --git a/tools/fibmap.c b/tools/fibmap.c
index 6b092f5..d17144a 100644
--- a/tools/fibmap.c
+++ b/tools/fibmap.c
@@ -1,4 +1,20 @@
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
+#define _XOPEN_SOURCE 600
+#define _DARWIN_C_SOURCE
+#define _FILE_OFFSET_BITS 64
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
+#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#endif
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -8,12 +24,25 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
+#endif
#include <libgen.h>
+#ifdef HAVE_LINUX_HDREG_H
#include <linux/hdreg.h>
+#endif
+#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
+#endif
+#ifdef __KERNEL__
#include <linux/fs.h>
+#endif
#include <inttypes.h>
+#include <f2fs_fs.h>
+
+#ifndef FIBMAP
+#define FIBMAP _IO(0x00, 1) /* bmap access */
+#endif
struct file_ext {
__u32 f_pos;
@@ -31,28 +60,42 @@ void print_ext(struct file_ext *ext)
ext->end_blk, ext->blk_count);
}
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
void print_stat(struct stat64 *st)
+#else
+void print_stat(struct stat *st)
+#endif
{
printf("--------------------------------------------\n");
printf("dev [%d:%d]\n", major(st->st_dev), minor(st->st_dev));
printf("ino [0x%8"PRIx64" : %"PRIu64"]\n",
st->st_ino, st->st_ino);
printf("mode [0x%8x : %d]\n", st->st_mode, st->st_mode);
- printf("nlink [0x%8lx : %ld]\n", st->st_nlink, st->st_nlink);
+ printf("nlink [0x%8lx : %ld]\n",
+ (unsigned long)st->st_nlink,
+ (long)st->st_nlink);
printf("uid [0x%8x : %d]\n", st->st_uid, st->st_uid);
printf("gid [0x%8x : %d]\n", st->st_gid, st->st_gid);
printf("size [0x%8"PRIx64" : %"PRIu64"]\n",
- st->st_size, st->st_size);
- printf("blksize [0x%8lx : %ld]\n", st->st_blksize, st->st_blksize);
+ (u64)st->st_size, (u64)st->st_size);
+ printf("blksize [0x%8lx : %ld]\n",
+ (unsigned long)st->st_blksize,
+ (long)st->st_blksize);
printf("blocks [0x%8"PRIx64" : %"PRIu64"]\n",
- st->st_blocks, st->st_blocks);
+ (u64)st->st_blocks, (u64)st->st_blocks);
printf("--------------------------------------------\n\n");
}
-void stat_bdev(struct stat64 *st, unsigned int *start_lba)
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+static void stat_bdev(struct stat64 *st, unsigned int *start_lba)
+#else
+static void stat_bdev(struct stat *st, unsigned int *start_lba)
+#endif
{
struct stat bdev_stat;
+#ifdef HDIO_GETGIO
struct hd_geometry geom;
+#endif
char devname[32] = { 0, };
char linkname[32] = { 0, };
int fd;
@@ -67,10 +110,14 @@ void stat_bdev(struct stat64 *st, unsigned int *start_lba)
goto out;
if (S_ISBLK(bdev_stat.st_mode)) {
+#ifdef HDIO_GETGIO
if (ioctl(fd, HDIO_GETGEO, &geom) < 0)
*start_lba = 0;
else
*start_lba = geom.start;
+#else
+ *start_lba = 0;
+#endif
}
if (readlink(devname, linkname, sizeof(linkname)) < 0)
@@ -90,7 +137,11 @@ int main(int argc, char *argv[])
int fd;
int ret = 0;
char *filename;
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
struct stat64 st;
+#else
+ struct stat st;
+#endif
int total_blks;
unsigned int i;
struct file_ext ext;
@@ -112,7 +163,11 @@ int main(int argc, char *argv[])
fsync(fd);
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
if (fstat64(fd, &st) < 0) {
+#else
+ if (fstat(fd, &st) < 0) {
+#endif
ret = errno;
perror(filename);
goto out;