diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2018-01-10 21:46:59 -0700 |
---|---|---|
committer | Linux Build Service Account <lnxbuild@localhost> | 2018-01-10 21:46:59 -0700 |
commit | 6074c18a74751dc1776f864e510af51b374ad2fd (patch) | |
tree | 8a6418b35861cddc232bb465b2f1166695b8f19a | |
parent | e1553b83ac7109f9c35dfb740aa4957445d3054e (diff) | |
parent | dcbae139f54fef47fa8cd2bbc7baf9de84a65652 (diff) | |
download | android_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
45 files changed, 6528 insertions, 734 deletions
@@ -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", +} @@ -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 @@ -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 @@ -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 = "afile_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 = "afile_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, §or_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; |