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