aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-12-07 21:07:45 -0800
committerHaibo Huang <hhb@google.com>2020-12-07 21:07:45 -0800
commita3476ce77923d90bfd560a9174892c480a4ba197 (patch)
tree304805cac36521b0c53e2f9129fbe05be9d52426
parente5df3f54ba36d8ffc4bcbbc8d6a64020574ab7c1 (diff)
parent4cfd6601941b4923979f7ad96af03089c373eecc (diff)
downloaderofs-utils-a3476ce77923d90bfd560a9174892c480a4ba197.tar.gz
Upgrade erofs-utils to v1.2
Test: make Change-Id: I8b02acd39eac735fed4dadd39c860691e5d85cbb
-rw-r--r--.gitignore9
-rw-r--r--ChangeLog14
-rw-r--r--METADATA10
-rw-r--r--Makefile.am4
-rw-r--r--README164
-rw-r--r--VERSION4
-rw-r--r--configure.ac24
-rw-r--r--fuse/Makefile.am10
-rw-r--r--fuse/dir.c103
-rw-r--r--fuse/main.c240
-rw-r--r--include/erofs/cache.h2
-rw-r--r--include/erofs/compress.h2
-rw-r--r--include/erofs/config.h9
-rw-r--r--include/erofs/decompress.h35
-rw-r--r--include/erofs/defs.h7
-rw-r--r--include/erofs/err.h2
-rw-r--r--include/erofs/hashtable.h2
-rw-r--r--include/erofs/inode.h2
-rw-r--r--include/erofs/internal.h99
-rw-r--r--include/erofs/io.h3
-rw-r--r--include/erofs/list.h2
-rw-r--r--include/erofs/print.h2
-rw-r--r--include/erofs/trace.h14
-rw-r--r--include/erofs/xattr.h2
-rw-r--r--include/erofs_fs.h6
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/cache.c2
-rw-r--r--lib/compress.c2
-rw-r--r--lib/config.c2
-rw-r--r--lib/data.c204
-rw-r--r--lib/decompress.c88
-rw-r--r--lib/inode.c87
-rw-r--r--lib/io.c18
-rw-r--r--lib/namei.c264
-rw-r--r--lib/super.c78
-rw-r--r--lib/xattr.c25
-rw-r--r--lib/zmap.c415
-rw-r--r--man/mkfs.erofs.16
-rw-r--r--mkfs/Makefile.am2
-rw-r--r--mkfs/main.c68
40 files changed, 1920 insertions, 116 deletions
diff --git a/.gitignore b/.gitignore
index 3a39a1e..8bdd505 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,16 @@
.*
*~
+*.[ao]
*.diff
-*.o
*.la
-*.a
+*.lo
+*.mod.c
+*.orig
*.patch
*.rej
+*.so
+*.so.dbg
+*.tar.*
#
# Generated files
diff --git a/ChangeLog b/ChangeLog
index 89a3868..947761a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+erofs-utils (1.2-1) unstable; urgency=medium
+
+ * This release includes the following features and bugfixes:
+ - (mkfs.erofs) support selinux file contexts;
+ - (mkfs.erofs) support $SOURCE_DATE_EPOCH;
+ - (mkfs.erofs) support a pre-defined UUID;
+ - (mkfs.erofs) fix random padding for reproducable builds;
+ - (mkfs.erofs) several fixes around hard links;
+ - (mkfs.erofs) minor code cleanups;
+ - (mkfs.erofs, AOSP) support Android fs_config;
+ - (experimental, disabled by default) add erofsfuse approach;
+
+ -- Gao Xiang <xiang@kernel.org> Tue, 06 Dec 2020 00:00:00 +0800
+
erofs-utils (1.1-1) unstable; urgency=low
* a maintenance release includes the following updates:
diff --git a/METADATA b/METADATA
index 4ec5f89..92af2e5 100644
--- a/METADATA
+++ b/METADATA
@@ -1,17 +1,15 @@
name: "erofs-utils"
-description:
- "EROFS Utilities"
-
+description: "EROFS Utilities"
third_party {
url {
type: GIT
value: "https://github.com/hsiangkao/erofs-utils"
}
- version: "v1.1"
+ version: "v1.2"
license_type: RESTRICTED
last_upgrade_date {
year: 2020
- month: 8
- day: 5
+ month: 12
+ day: 7
}
}
diff --git a/Makefile.am b/Makefile.am
index 1d20577..b804aa9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,3 +4,7 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man lib mkfs
+if ENABLE_FUSE
+SUBDIRS += fuse
+endif
+
diff --git a/README b/README
index 60b8eed..0001a67 100644
--- a/README
+++ b/README
@@ -1,30 +1,20 @@
erofs-utils
===========
-erofs-utils includes user-space tools for erofs filesystem images.
-Currently only mkfs.erofs is available.
+erofs-utils includes user-space tools for erofs filesystem.
+Currently mkfs.erofs and erofsfuse (experimental) are available.
-mkfs.erofs
-----------
-
-It can create 2 primary kinds of erofs images: (un)compressed.
-
- - For compressed images, it's able to use several compression
- algorithms, but lz4(hc) are only supported due to the current
- linux kernel implementation.
-
- - For uncompressed images, it can decide whether the last page of
- a file should be inlined or not properly [1].
+Dependencies & build
+--------------------
-Dependencies
-~~~~~~~~~~~~
+ lz4 1.8.0+ for lz4 enabled [2], lz4 1.9.3+ highly recommended [4][5].
- lz4-1.8.0+ for lz4 enabled [2], lz4-1.9.0+ recommended
+ libfuse 2.6+ for erofsfuse enabled as a plus.
How to build for lz4-1.9.0 or above
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To build you can run the following commands in order:
+To build, you can run the following commands in order:
::
@@ -32,23 +22,44 @@ To build you can run the following commands in order:
$ ./configure
$ make
-mkfs.erofs binary will be generated under mkfs folder. There are still
-some issues which affect the stability of LZ4_compress_destSize()
-* they have impacts on lz4 only rather than lz4HC * [3].
+mkfs.erofs binary will be generated under mkfs folder.
+
+* For lz4 < 1.9.2, there are some stability issues about
+ LZ4_compress_destSize(). (lz4hc isn't impacted) [3].
+
+** For lz4 = 1.9.2, there is a noticeable regression about
+ LZ4_decompress_safe_partial() [5], which impacts erofsfuse
+ functionality for legacy images (without 0PADDING).
How to build for lz4-1.8.0~1.8.3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-For these old lz4 versions, lz4hc algorithm cannot be supported without
-lz4 static libary due to LZ4_compress_HC_destSize unstable api usage,
-which means only lz4 algrithm is available if lz4 static library isn't found.
+For these old lz4 versions, lz4hc algorithm cannot be supported
+without lz4-static installed due to LZ4_compress_HC_destSize()
+unstable api usage, which means lz4 will only be available if
+lz4-static isn't found.
-On Fedora, static lz4 can be installed using:
+On Fedora, lz4-static can be installed by using:
yum install lz4-static.x86_64
-However, it's not recommended to use those versions since there were bugs
-in these compressors, see [2] [3] as well.
+However, it's still not recommended using those versions directly
+since there are serious bugs in these compressors, see [2] [3] [4]
+as well.
+
+mkfs.erofs
+----------
+
+two main kinds of erofs images can be generated: (un)compressed.
+
+ - For uncompressed images, there will be none of compression
+ files in these images. However, it can decide whether the tail
+ block of a file should be inlined or not properly [1].
+
+ - For compressed images, it will try to use lz4(hc) algorithm
+ first for each regular file and see if storage space can be
+ saved with compression. If not, fallback to an uncompressed
+ file.
How to generate erofs images
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -56,44 +67,79 @@ How to generate erofs images
Currently lz4 and lz4hc are available for compression, e.g.
$ mkfs.erofs -zlz4hc foo.erofs.img foo/
-Or leave all files uncompressed as a option:
+Or leave all files uncompressed as an option:
$ mkfs.erofs foo.erofs.img foo/
How to generate legacy erofs images
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Decompression inplace and compacted indexes have been introduced in
-linux-5.3, which are not backward-compatible with older kernels.
+linux-5.3, which are not forward-compatible with older kernels.
In order to generate _legacy_ erofs images for old kernels,
-add "-E legacy-compress" to the command line, e.g.
+consider adding "-E legacy-compress" to the command line, e.g.
$ mkfs.erofs -E legacy-compress -zlz4hc foo.erofs.img foo/
-
-Known issues
-~~~~~~~~~~~~
-
-1. LZ4HC cannot compress long zeroed buffer properly with
- LZ4_compress_HC_destSize()
- https://github.com/lz4/lz4/issues/784
-
Obsoleted erofs.mkfs
~~~~~~~~~~~~~~~~~~~~
-There is an original erofs.mkfs version developped by Li Guifu,
+There is an original erofs.mkfs version developed by Li Guifu,
which was replaced by the new erofs-utils implementation.
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git -b obsoleted_mkfs
-It may still be useful since new erofs-utils has not been widely used in
-commercial products. However, if that happens, please report bug to us
-as well.
+PLEASE NOTE: This version is highly _NOT recommended_ now.
+
+erofsfuse (experimental, unstable)
+----------------------------------
+
+erofsfuse is introduced to support erofs format for various platforms
+(including older linux kernels) and new on-disk features iteration.
+It can also be used as an unpacking tool for unprivileged users.
+
+It supports fixed-sized output decompression *without* any in-place
+I/O or in-place decompression optimization. Also like the other FUSE
+implementations, it suffers from most common performance issues (e.g.
+significant I/O overhead, double caching, etc.)
+
+Therefore, NEVER use it if performance is the top concern.
+
+Note that xattr & ACL aren't implemented yet due to the current Android
+use-case vs limited time. If you have some interest, contribution is,
+as always, welcome.
+
+How to build erofsfuse
+~~~~~~~~~~~~~~~~~~~~~~
+
+It's disabled by default as an experimental feature for now, to enable
+and build it manually:
+
+ $ ./configure --enable-fuse
+ $ make
+
+erofsfuse binary will be generated under fuse folder.
+
+How to mount an erofs image with erofsfuse
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As the other FUSE implementations, it's quite simple to mount with
+erofsfuse, e.g.:
+ $ erofsfuse foo.erofs.img foo/
+
+Alternatively, to make it run in foreground (with debugging level 3):
+ $ erofsfuse -f --dbglevel=3 foo.erofs.img foo/
+
+To debug erofsfuse (also automatically run in foreground):
+ $ erofsfuse -d foo.erofs.img foo/
+
+To unmount an erofsfuse mountpoint as a non-root user:
+ $ fusermount -u foo/
Contribution
------------
-erofs-utils is a GPLv2+ project as a part of erofs file system,
+erofs-utils is under GPLv2+ as a part of erofs project,
feel free to send patches or feedback to us.
To:
@@ -109,19 +155,20 @@ Cc:
Comments
--------
-[1] According to the erofs on-disk format, the last page of files could
- be inlined aggressively with its metadata in order to reduce the I/O
- overhead and save the storage space.
+[1] According to the erofs on-disk format, the tail block of files
+ could be inlined aggressively with its metadata in order to reduce
+ the I/O overhead and save the storage space (called tail-packing).
-[2] There was a bug until lz4-1.8.3, which can crash erofs-utils randomly.
- Fortunately bugfix by our colleague Qiuyang Sun was merged in lz4-1.9.0.
+[2] There was a bug until lz4-1.8.3, which can crash erofs-utils
+ randomly. Fortunately bugfix by our colleague Qiuyang Sun was
+ merged in lz4-1.9.0.
For more details, please refer to
https://github.com/lz4/lz4/commit/660d21272e4c8a0f49db5fc1e6853f08713dff82
-[3] There are many crash fixes merged to lz4 1.9.2 for LZ4_compress_destSize(),
- and I once ran into some crashs due to those issues.
- * Again lz4HC is not effected for this section. *
+[3] There were many bugfixes merged into lz4-1.9.2 for
+ LZ4_compress_destSize(), and I once ran into some crashs due to
+ those issues. * Again lz4hc is not affected. *
[LZ4_compress_destSize] Allow 2 more bytes of match length
https://github.com/lz4/lz4/commit/690009e2c2f9e5dcb0d40e7c0c40610ce6006eda
@@ -142,5 +189,20 @@ Comments
preferred to use latest upstream lz4 library (although some regressions
could happen since new features are also introduced to latest upstream
version as well) or backport all stable bugfixes to old stable versions,
- e.g. our unoffical lz4 fork: https://github.com/erofs/lz4
+ e.g. our unofficial lz4 fork: https://github.com/erofs/lz4
+
+[4] LZ4HC didn't compress long zeroed buffer properly with
+ LZ4_compress_HC_destSize()
+ https://github.com/lz4/lz4/issues/784
+
+ which has been resolved in
+ https://github.com/lz4/lz4/commit/e7fe105ac6ed02019d34731d2ba3aceb11b51bb1
+
+ and already included in lz4-1.9.3, see:
+ https://github.com/lz4/lz4/releases/tag/v1.9.3
+
+[5] LZ4_decompress_safe_partial is broken in 1.9.2
+ https://github.com/lz4/lz4/issues/783
+
+ which is also resolved in lz4-1.9.3.
diff --git a/VERSION b/VERSION
index 36cbcfd..aabe0d1 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-1.1
-2020-04-14
+1.2
+2020-12-06
diff --git a/configure.ac b/configure.ac
index 0f40a84..d5fdfb8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,10 @@ AC_ARG_ENABLE(lz4,
[AS_HELP_STRING([--disable-lz4], [disable LZ4 compression support @<:@default=enabled@:>@])],
[enable_lz4="$enableval"], [enable_lz4="yes"])
+AC_ARG_ENABLE(fuse,
+ [AS_HELP_STRING([--enable-fuse], [enable erofsfuse @<:@default=no@:>@])],
+ [enable_fuse="$enableval"], [enable_fuse="no"])
+
AC_ARG_WITH(uuid,
[AS_HELP_STRING([--without-uuid],
[Ignore presence of libuuid and disable uuid support @<:@default=enabled@:>@])])
@@ -183,6 +187,20 @@ AS_IF([test "x$with_selinux" != "xno"], [
LIBS="${saved_LIBS}"
CPPFLAGS="${saved_CPPFLAGS}"], [have_selinux="no"])
+# Configure fuse
+AS_IF([test "x$enable_fuse" != "xno"], [
+ PKG_CHECK_MODULES([libfuse], [fuse >= 2.6])
+ # Paranoia: don't trust the result reported by pkgconfig before trying out
+ saved_LIBS="$LIBS"
+ saved_CPPFLAGS=${CPPFLAGS}
+ CPPFLAGS="${libfuse_CFLAGS} ${CPPFLAGS}"
+ LIBS="${libfuse_LIBS} $LIBS"
+ AC_CHECK_LIB(fuse, fuse_main, [
+ have_fuse="yes" ], [
+ AC_MSG_ERROR([libfuse (>= 2.6) doesn't work properly])])
+ LIBS="${saved_LIBS}"
+ CPPFLAGS="${saved_CPPFLAGS}"], [have_fuse="no"])
+
# Configure lz4
test -z $LZ4_LIBS && LZ4_LIBS='-llz4'
@@ -218,6 +236,7 @@ fi
# Set up needed symbols, conditionals and compiler/linker flags
AM_CONDITIONAL([ENABLE_LZ4], [test "x${have_lz4}" = "xyes"])
AM_CONDITIONAL([ENABLE_LZ4HC], [test "x${have_lz4hc}" = "xyes"])
+AM_CONDITIONAL([ENABLE_FUSE], [test "x${have_fuse}" = "xyes"])
if test "x$have_uuid" = "xyes"; then
AC_DEFINE([HAVE_LIBUUID], 1, [Define to 1 if libuuid is found])
@@ -240,11 +259,14 @@ if test "x${have_lz4}" = "xyes"; then
else
test -z "${with_lz4_libdir}" || LZ4_LIBS="-R${with_lz4_libdir} $LZ4_LIBS"
fi
+ liblz4_LIBS="${LZ4_LIBS}"
fi
+AC_SUBST([liblz4_LIBS])
AC_CONFIG_FILES([Makefile
man/Makefile
lib/Makefile
- mkfs/Makefile])
+ mkfs/Makefile
+ fuse/Makefile])
AC_OUTPUT
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
new file mode 100644
index 0000000..f14f6fd
--- /dev/null
+++ b/fuse/Makefile.am
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Makefile.am
+
+AUTOMAKE_OPTIONS = foreign
+bin_PROGRAMS = erofsfuse
+erofsfuse_SOURCES = dir.c main.c
+erofsfuse_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
+erofsfuse_CFLAGS += -DFUSE_USE_VERSION=26 ${libfuse_CFLAGS}
+erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la ${libfuse_LIBS} ${liblz4_LIBS}
+
diff --git a/fuse/dir.c b/fuse/dir.c
new file mode 100644
index 0000000..f8fa0f6
--- /dev/null
+++ b/fuse/dir.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/fuse/dir.c
+ *
+ * Created by Li Guifu <blucerlee@gmail.com>
+ */
+#include <fuse.h>
+#include <fuse_opt.h>
+
+#include "erofs/internal.h"
+#include "erofs/print.h"
+
+static int erofs_fill_dentries(struct erofs_inode *dir,
+ fuse_fill_dir_t filler, void *buf,
+ void *dblk, unsigned int nameoff,
+ unsigned int maxsize)
+{
+ struct erofs_dirent *de = dblk;
+ const struct erofs_dirent *end = dblk + nameoff;
+ char namebuf[EROFS_NAME_LEN + 1];
+
+ while (de < end) {
+ const char *de_name;
+ unsigned int de_namelen;
+
+ nameoff = le16_to_cpu(de->nameoff);
+ de_name = (char *)dblk + nameoff;
+
+ /* the last dirent in the block? */
+ if (de + 1 >= end)
+ de_namelen = strnlen(de_name, maxsize - nameoff);
+ else
+ de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
+
+ /* a corrupted entry is found */
+ if (nameoff + de_namelen > maxsize ||
+ de_namelen > EROFS_NAME_LEN) {
+ erofs_err("bogus dirent @ nid %llu", dir->nid | 0ULL);
+ DBG_BUGON(1);
+ return -EFSCORRUPTED;
+ }
+
+ memcpy(namebuf, de_name, de_namelen);
+ namebuf[de_namelen] = '\0';
+
+ filler(buf, namebuf, NULL, 0);
+ ++de;
+ }
+ return 0;
+}
+
+int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int ret;
+ struct erofs_inode dir;
+ char dblk[EROFS_BLKSIZ];
+ erofs_off_t pos;
+
+ erofs_dbg("readdir:%s offset=%llu", path, (long long)offset);
+
+ ret = erofs_ilookup(path, &dir);
+ if (ret)
+ return ret;
+
+ erofs_dbg("path=%s nid = %llu", path, dir.nid | 0ULL);
+
+ if (!S_ISDIR(dir.i_mode))
+ return -ENOTDIR;
+
+ if (!dir.i_size)
+ return 0;
+
+ pos = 0;
+ while (pos < dir.i_size) {
+ unsigned int nameoff, maxsize;
+ struct erofs_dirent *de;
+
+ maxsize = min_t(unsigned int, EROFS_BLKSIZ,
+ dir.i_size - pos);
+ ret = erofs_pread(&dir, dblk, maxsize, pos);
+ if (ret)
+ return ret;
+
+ de = (struct erofs_dirent *)dblk;
+ nameoff = le16_to_cpu(de->nameoff);
+ if (nameoff < sizeof(struct erofs_dirent) ||
+ nameoff >= PAGE_SIZE) {
+ erofs_err("invalid de[0].nameoff %u @ nid %llu",
+ nameoff, dir.nid | 0ULL);
+ ret = -EFSCORRUPTED;
+ break;
+ }
+
+ ret = erofs_fill_dentries(&dir, filler, buf,
+ dblk, nameoff, maxsize);
+ if (ret)
+ break;
+ pos += maxsize;
+ }
+ return 0;
+}
+
diff --git a/fuse/main.c b/fuse/main.c
new file mode 100644
index 0000000..1e24efe
--- /dev/null
+++ b/fuse/main.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/fuse/main.c
+ *
+ * Created by Li Guifu <blucerlee@gmail.com>
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <libgen.h>
+#include <fuse.h>
+#include <fuse_opt.h>
+
+#include "erofs/config.h"
+#include "erofs/print.h"
+#include "erofs/io.h"
+
+int erofsfuse_readdir(const char *path, void *buffer, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi);
+
+static void *erofsfuse_init(struct fuse_conn_info *info)
+{
+ erofs_info("Using FUSE protocol %d.%d", info->proto_major, info->proto_minor);
+ return NULL;
+}
+
+static int erofsfuse_open(const char *path, struct fuse_file_info *fi)
+{
+ erofs_dbg("open path=%s", path);
+
+ if ((fi->flags & O_ACCMODE) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
+static int erofsfuse_getattr(const char *path, struct stat *stbuf)
+{
+ struct erofs_inode vi = {};
+ int ret;
+
+ erofs_dbg("getattr(%s)", path);
+ ret = erofs_ilookup(path, &vi);
+ if (ret)
+ return -ENOENT;
+
+ stbuf->st_mode = vi.i_mode;
+ stbuf->st_nlink = vi.i_nlink;
+ stbuf->st_size = vi.i_size;
+ stbuf->st_blocks = roundup(vi.i_size, EROFS_BLKSIZ) >> 9;
+ stbuf->st_uid = vi.i_uid;
+ stbuf->st_gid = vi.i_gid;
+ if (S_ISBLK(vi.i_mode) || S_ISCHR(vi.i_mode))
+ stbuf->st_rdev = vi.u.i_rdev;
+ stbuf->st_ctime = vi.i_ctime;
+ stbuf->st_mtime = stbuf->st_ctime;
+ stbuf->st_atime = stbuf->st_ctime;
+ return 0;
+}
+
+static int erofsfuse_read(const char *path, char *buffer,
+ size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int ret;
+ struct erofs_inode vi;
+
+ erofs_dbg("path:%s size=%zd offset=%llu", path, size, (long long)offset);
+
+ ret = erofs_ilookup(path, &vi);
+ if (ret)
+ return ret;
+
+ ret = erofs_pread(&vi, buffer, size, offset);
+ if (ret)
+ return ret;
+ return size;
+}
+
+static int erofsfuse_readlink(const char *path, char *buffer, size_t size)
+{
+ int ret = erofsfuse_read(path, buffer, size, 0, NULL);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static struct fuse_operations erofs_ops = {
+ .readlink = erofsfuse_readlink,
+ .getattr = erofsfuse_getattr,
+ .readdir = erofsfuse_readdir,
+ .open = erofsfuse_open,
+ .read = erofsfuse_read,
+ .init = erofsfuse_init,
+};
+
+static struct options {
+ const char *disk;
+ const char *mountpoint;
+ unsigned int debug_lvl;
+ bool show_help;
+ bool odebug;
+} fusecfg;
+
+#define OPTION(t, p) \
+ { t, offsetof(struct options, p), 1 }
+static const struct fuse_opt option_spec[] = {
+ OPTION("--dbglevel=%u", debug_lvl),
+ OPTION("--help", show_help),
+ FUSE_OPT_END
+};
+
+#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
+
+static void usage(void)
+{
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ fputs("usage: [options] IMAGE MOUNTPOINT\n\n"
+ "Options:\n"
+ " --dbglevel=# set output message level to # (maximum 9)\n"
+#if FUSE_MAJOR_VERSION < 3
+ " --help display this help and exit\n"
+#endif
+ "\n", stderr);
+
+#if FUSE_MAJOR_VERSION >= 3
+ fuse_cmdline_help();
+#else
+ fuse_opt_add_arg(&args, ""); /* progname */
+ fuse_opt_add_arg(&args, "-ho"); /* progname */
+ fuse_parse_cmdline(&args, NULL, NULL, NULL);
+#endif
+ exit(EXIT_FAILURE);
+}
+
+static void erofsfuse_dumpcfg(void)
+{
+ erofs_dump("disk: %s\n", fusecfg.disk);
+ erofs_dump("mountpoint: %s\n", fusecfg.mountpoint);
+ erofs_dump("dbglevel: %u\n", cfg.c_dbg_lvl);
+}
+
+static int optional_opt_func(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ switch (key) {
+ case FUSE_OPT_KEY_NONOPT:
+ if (fusecfg.mountpoint)
+ return -1; /* Too many args */
+
+ if (!fusecfg.disk) {
+ fusecfg.disk = strdup(arg);
+ return 0;
+ }
+ if (!fusecfg.mountpoint)
+ fusecfg.mountpoint = strdup(arg);
+ case FUSE_OPT_KEY_OPT:
+ if (!strcmp(arg, "-d"))
+ fusecfg.odebug = true;
+ break;
+ default:
+ DBG_BUGON(1);
+ break;
+ }
+ return 1;
+}
+
+static void signal_handle_sigsegv(int signal)
+{
+ void *array[10];
+ size_t nptrs;
+ char **strings;
+ size_t i;
+
+ erofs_dump("========================================\n");
+ erofs_dump("Segmentation Fault. Starting backtrace:\n");
+ nptrs = backtrace(array, 10);
+ strings = backtrace_symbols(array, nptrs);
+ if (strings) {
+ for (i = 0; i < nptrs; i++)
+ erofs_dump("%s\n", strings[i]);
+ free(strings);
+ }
+ erofs_dump("========================================\n");
+ abort();
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+
+ erofs_init_configure();
+ fprintf(stderr, "%s %s\n", basename(argv[0]), cfg.c_version);
+
+ if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
+ fprintf(stderr, "failed to initialize signals\n");
+ ret = -errno;
+ goto err;
+ }
+
+ /* parse options */
+ ret = fuse_opt_parse(&args, &fusecfg, option_spec, optional_opt_func);
+ if (ret)
+ goto err;
+
+ if (fusecfg.show_help || !fusecfg.mountpoint)
+ usage();
+ cfg.c_dbg_lvl = fusecfg.debug_lvl;
+
+ if (fusecfg.odebug && cfg.c_dbg_lvl < EROFS_DBG)
+ cfg.c_dbg_lvl = EROFS_DBG;
+
+ erofsfuse_dumpcfg();
+ ret = dev_open_ro(fusecfg.disk);
+ if (ret) {
+ fprintf(stderr, "failed to open: %s\n", fusecfg.disk);
+ goto err_fuse_free_args;
+ }
+
+ ret = erofs_read_superblock();
+ if (ret) {
+ fprintf(stderr, "failed to read erofs super block\n");
+ goto err_dev_close;
+ }
+
+ ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
+err_dev_close:
+ dev_close();
+err_fuse_free_args:
+ fuse_opt_free_args(&args);
+err:
+ erofs_exit_configure();
+ return ret ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/include/erofs/cache.h b/include/erofs/cache.h
index 10a6aac..8c171f5 100644
--- a/include/erofs/cache.h
+++ b/include/erofs/cache.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/cache.h
+ * erofs-utils/include/erofs/cache.h
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/compress.h b/include/erofs/compress.h
index fa91873..952f287 100644
--- a/include/erofs/compress.h
+++ b/include/erofs/compress.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/compress.h
+ * erofs-utils/include/erofs/compress.h
*
* Copyright (C) 2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/config.h b/include/erofs/config.h
index 9902a08..02ddf59 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/config.h
+ * erofs-utils/include/erofs/config.h
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
@@ -29,11 +29,18 @@ enum {
FORCE_INODE_EXTENDED,
};
+enum {
+ TIMESTAMP_NONE,
+ TIMESTAMP_FIXED,
+ TIMESTAMP_CLAMPING,
+};
+
struct erofs_configure {
const char *c_version;
int c_dbg_lvl;
bool c_dry_run;
bool c_legacy_compress;
+ char c_timeinherit;
#ifdef HAVE_LIBSELINUX
struct selabel_handle *sehnd;
diff --git a/include/erofs/decompress.h b/include/erofs/decompress.h
new file mode 100644
index 0000000..beaac35
--- /dev/null
+++ b/include/erofs/decompress.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-utils/include/erofs/decompress.h
+ *
+ * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
+ * Created by Huang Jianan <huangjianan@oppo.com>
+ */
+#ifndef __EROFS_DECOMPRESS_H
+#define __EROFS_DECOMPRESS_H
+
+#include "internal.h"
+
+enum {
+ Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
+ Z_EROFS_COMPRESSION_RUNTIME_MAX
+};
+
+struct z_erofs_decompress_req {
+ char *in, *out;
+
+ /*
+ * initial decompressed bytes that need to be skipped
+ * when finally copying to output buffer
+ */
+ unsigned int decodedskip;
+ unsigned int inputsize, decodedlength;
+
+ /* indicate the algorithm will be used for decompression */
+ unsigned int alg;
+ bool partial_decoding;
+};
+
+int z_erofs_decompress(struct z_erofs_decompress_req *rq);
+
+#endif
diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 33320db..b54cd9d 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/defs.h
+ * erofs-utils/include/erofs/defs.h
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
@@ -170,5 +170,10 @@ typedef int64_t s64;
#define __maybe_unused __attribute__((__unused__))
#endif
+static inline u32 get_unaligned_le32(const u8 *p)
+{
+ return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+}
+
#endif
diff --git a/include/erofs/err.h b/include/erofs/err.h
index fd4c873..da3b681 100644
--- a/include/erofs/err.h
+++ b/include/erofs/err.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/err.h
+ * erofs-utils/include/erofs/err.h
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/hashtable.h b/include/erofs/hashtable.h
index ab57b56..7e47189 100644
--- a/include/erofs/hashtable.h
+++ b/include/erofs/hashtable.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * erofs_utils/include/erofs/hashtable.h
+ * erofs-utils/include/erofs/hashtable.h
*
* Original code taken from 'linux/include/linux/hash{,table}.h'
*/
diff --git a/include/erofs/inode.h b/include/erofs/inode.h
index 43aee93..5a7f5f1 100644
--- a/include/erofs/inode.h
+++ b/include/erofs/inode.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/inode.h
+ * erofs-utils/include/erofs/inode.h
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index bc77c43..ac5b270 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/internal.h
+ * erofs-utils/include/erofs/internal.h
*
* Copyright (C) 2019 HUAWEI, Inc.
* http://www.huawei.com/
@@ -36,6 +36,8 @@ typedef unsigned short umode_t;
#error incompatible PAGE_SIZE is already defined
#endif
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
#define LOG_BLOCK_SIZE (12)
#define EROFS_BLKSIZ (1U << LOG_BLOCK_SIZE)
@@ -59,6 +61,8 @@ typedef u32 erofs_blk_t;
struct erofs_buffer_head;
struct erofs_sb_info {
+ u64 blocks;
+
erofs_blk_t meta_blkaddr;
erofs_blk_t xattr_blkaddr;
@@ -66,12 +70,25 @@ struct erofs_sb_info {
u32 feature_incompat;
u64 build_time;
u32 build_time_nsec;
+
+ unsigned char islotbits;
+
+ /* what we really care is nid, rather than ino.. */
+ erofs_nid_t root_nid;
+ /* used for statfs, f_files - f_favail */
+ u64 inos;
+
u8 uuid[16];
};
/* global sbi */
extern struct erofs_sb_info sbi;
+static inline erofs_off_t iloc(erofs_nid_t nid)
+{
+ return blknr_to_addr(sbi.meta_blkaddr) + (nid << sbi.islotbits);
+}
+
#define EROFS_FEATURE_FUNCS(name, compat, feature) \
static inline bool erofs_sb_has_##name(void) \
{ \
@@ -89,9 +106,18 @@ static inline void erofs_sb_clear_##name(void) \
EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING)
EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
+#define EROFS_I_EA_INITED (1 << 0)
+#define EROFS_I_Z_INITED (1 << 1)
+
struct erofs_inode {
struct list_head i_hash, i_subdirs, i_xattrs;
+ union {
+ /* (erofsfuse) runtime flags */
+ unsigned int flags;
+ /* (mkfs.erofs) device ID containing source file */
+ u32 dev;
+ };
unsigned int i_count;
struct erofs_inode *i_parent;
@@ -126,7 +152,16 @@ struct erofs_inode {
struct erofs_buffer_head *bh_inline, *bh_data;
void *idata;
- void *compressmeta;
+
+ union {
+ void *compressmeta;
+ struct {
+ uint16_t z_advise;
+ uint8_t z_algorithmtype[2];
+ uint8_t z_logical_clusterbits;
+ uint8_t z_physical_clusterbits[2];
+ };
+ };
#ifdef WITH_ANDROID
uint64_t capabilities;
#endif
@@ -137,6 +172,24 @@ static inline bool is_inode_layout_compression(struct erofs_inode *inode)
return erofs_inode_is_data_compressed(inode->datalayout);
}
+static inline unsigned int erofs_bitrange(unsigned int value, unsigned int bit,
+ unsigned int bits)
+{
+ return (value >> bit) & ((1 << bits) - 1);
+}
+
+static inline unsigned int erofs_inode_version(unsigned int value)
+{
+ return erofs_bitrange(value, EROFS_I_VERSION_BIT,
+ EROFS_I_VERSION_BITS);
+}
+
+static inline unsigned int erofs_inode_datalayout(unsigned int value)
+{
+ return erofs_bitrange(value, EROFS_I_DATALAYOUT_BIT,
+ EROFS_I_DATALAYOUT_BITS);
+}
+
#define IS_ROOT(x) ((x) == (x)->i_parent)
struct erofs_dentry {
@@ -169,5 +222,47 @@ static inline const char *erofs_strerror(int err)
return msg;
}
+enum {
+ BH_Meta,
+ BH_Mapped,
+ BH_Zipped,
+ BH_FullMapped,
+};
+
+/* Has a disk mapping */
+#define EROFS_MAP_MAPPED (1 << BH_Mapped)
+/* Located in metadata (could be copied from bd_inode) */
+#define EROFS_MAP_META (1 << BH_Meta)
+/* The extent has been compressed */
+#define EROFS_MAP_ZIPPED (1 << BH_Zipped)
+/* The length of extent is full */
+#define EROFS_MAP_FULL_MAPPED (1 << BH_FullMapped)
+
+struct erofs_map_blocks {
+ char mpage[EROFS_BLKSIZ];
+
+ erofs_off_t m_pa, m_la;
+ u64 m_plen, m_llen;
+
+ unsigned int m_flags;
+ erofs_blk_t index;
+};
+
+/* super.c */
+int erofs_read_superblock(void);
+
+/* namei.c */
+int erofs_ilookup(const char *path, struct erofs_inode *vi);
+
+/* data.c */
+int erofs_pread(struct erofs_inode *inode, char *buf,
+ erofs_off_t count, erofs_off_t offset);
+/* zmap.c */
+int z_erofs_fill_inode(struct erofs_inode *vi);
+int z_erofs_map_blocks_iter(struct erofs_inode *vi,
+ struct erofs_map_blocks *map);
+
+#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
+
#endif
diff --git a/include/erofs/io.h b/include/erofs/io.h
index e0ca8d9..5574245 100644
--- a/include/erofs/io.h
+++ b/include/erofs/io.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/io.h
+ * erofs-utils/include/erofs/io.h
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
@@ -17,6 +17,7 @@
#endif
int dev_open(const char *devname);
+int dev_open_ro(const char *dev);
void dev_close(void);
int dev_write(const void *buf, u64 offset, size_t len);
int dev_read(void *buf, u64 offset, size_t len);
diff --git a/include/erofs/list.h b/include/erofs/list.h
index e290843..3572726 100644
--- a/include/erofs/list.h
+++ b/include/erofs/list.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/list.h
+ * erofs-utils/include/erofs/list.h
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/print.h b/include/erofs/print.h
index e29fc1d..6b79074 100644
--- a/include/erofs/print.h
+++ b/include/erofs/print.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * erofs_utils/include/erofs/print.h
+ * erofs-utils/include/erofs/print.h
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/include/erofs/trace.h b/include/erofs/trace.h
new file mode 100644
index 0000000..5a12da7
--- /dev/null
+++ b/include/erofs/trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-utils/include/erofs/trace.h
+ *
+ * Copyright (C) 2020 Gao Xiang <hsiangkao@aol.com>
+ */
+#ifndef __EROFS_TRACE_H
+#define __EROFS_TRACE_H
+
+#define trace_erofs_map_blocks_flatmode_enter(inode, map, flags) ((void)0)
+#define trace_erofs_map_blocks_flatmode_exit(inode, map, flags, ret) ((void)0)
+
+#endif
+
diff --git a/include/erofs/xattr.h b/include/erofs/xattr.h
index 9e2e1ea..197fe25 100644
--- a/include/erofs/xattr.h
+++ b/include/erofs/xattr.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/include/erofs/xattr.h
+ * erofs-utils/include/erofs/xattr.h
*
* Originally contributed by an anonymous person,
* heavily changed by Li Guifu <blucerlee@gmail.com>
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index bcc4f0c..a69f179 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only OR Apache-2.0 */
/*
- * erofs_utils/include/erofs_fs.h
+ * erofs-utils/include/erofs_fs.h
* EROFS (Enhanced ROM File System) on-disk format definition
*
* Copyright (C) 2017-2018 HUAWEI, Inc.
@@ -279,6 +279,10 @@ struct z_erofs_vle_decompressed_index {
} di_u;
};
+#define Z_EROFS_VLE_LEGACY_INDEX_ALIGN(size) \
+ (round_up(size, sizeof(struct z_erofs_vle_decompressed_index)) + \
+ sizeof(struct z_erofs_map_header) + Z_EROFS_VLE_LEGACY_HEADER_PADDING)
+
#define Z_EROFS_VLE_EXTENT_ALIGN(size) round_up(size, \
sizeof(struct z_erofs_vle_decompressed_index))
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e4b51e6..f21dc35 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -2,8 +2,8 @@
# Makefile.am
noinst_LTLIBRARIES = liberofs.la
-liberofs_la_SOURCES = config.c io.c cache.c inode.c xattr.c \
- compress.c compressor.c exclude.c
+liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \
+ namei.c data.c compress.c compressor.c zmap.c decompress.c
liberofs_la_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
if ENABLE_LZ4
liberofs_la_CFLAGS += ${LZ4_CFLAGS}
diff --git a/lib/cache.c b/lib/cache.c
index e61b201..0d5c4a5 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/cache.c
+ * erofs-utils/lib/cache.c
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/lib/compress.c b/lib/compress.c
index 6cc68ed..86db940 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/compress.c
+ * erofs-utils/lib/compress.c
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/lib/config.c b/lib/config.c
index da0c260..3155112 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/config.c
+ * erofs-utils/lib/config.c
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
diff --git a/lib/data.c b/lib/data.c
new file mode 100644
index 0000000..3781846
--- /dev/null
+++ b/lib/data.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/lib/data.c
+ *
+ * Copyright (C) 2020 Gao Xiang <hsiangkao@aol.com>
+ * Compression support by Huang Jianan <huangjianan@oppo.com>
+ */
+#include "erofs/print.h"
+#include "erofs/internal.h"
+#include "erofs/io.h"
+#include "erofs/trace.h"
+#include "erofs/decompress.h"
+
+static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
+ struct erofs_map_blocks *map,
+ int flags)
+{
+ int err = 0;
+ erofs_blk_t nblocks, lastblk;
+ u64 offset = map->m_la;
+ struct erofs_inode *vi = inode;
+ bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
+
+ trace_erofs_map_blocks_flatmode_enter(inode, map, flags);
+
+ nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
+ lastblk = nblocks - tailendpacking;
+
+ if (offset >= inode->i_size) {
+ /* leave out-of-bound access unmapped */
+ map->m_flags = 0;
+ goto out;
+ }
+
+ /* there is no hole in flatmode */
+ map->m_flags = EROFS_MAP_MAPPED;
+
+ if (offset < blknr_to_addr(lastblk)) {
+ map->m_pa = blknr_to_addr(vi->u.i_blkaddr) + map->m_la;
+ map->m_plen = blknr_to_addr(lastblk) - offset;
+ } else if (tailendpacking) {
+ /* 2 - inode inline B: inode, [xattrs], inline last blk... */
+ map->m_pa = iloc(vi->nid) + vi->inode_isize +
+ vi->xattr_isize + erofs_blkoff(map->m_la);
+ map->m_plen = inode->i_size - offset;
+
+ /* inline data should be located in one meta block */
+ if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) {
+ erofs_err("inline data cross block boundary @ nid %" PRIu64,
+ vi->nid);
+ DBG_BUGON(1);
+ err = -EFSCORRUPTED;
+ goto err_out;
+ }
+
+ map->m_flags |= EROFS_MAP_META;
+ } else {
+ erofs_err("internal error @ nid: %" PRIu64 " (size %llu), m_la 0x%" PRIx64,
+ vi->nid, (unsigned long long)inode->i_size, map->m_la);
+ DBG_BUGON(1);
+ err = -EIO;
+ goto err_out;
+ }
+
+out:
+ map->m_llen = map->m_plen;
+
+err_out:
+ trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0);
+ return err;
+}
+
+static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
+ erofs_off_t size, erofs_off_t offset)
+{
+ struct erofs_map_blocks map = {
+ .index = UINT_MAX,
+ };
+ int ret;
+ erofs_off_t ptr = offset;
+
+ while (ptr < offset + size) {
+ erofs_off_t eend;
+
+ map.m_la = ptr;
+ ret = erofs_map_blocks_flatmode(inode, &map, 0);
+ if (ret)
+ return ret;
+
+ DBG_BUGON(map.m_plen != map.m_llen);
+
+ if (!(map.m_flags & EROFS_MAP_MAPPED)) {
+ if (!map.m_llen) {
+ ptr = offset + size;
+ continue;
+ }
+ ptr = map.m_la + map.m_llen;
+ continue;
+ }
+
+ /* trim extent */
+ eend = min(offset + size, map.m_la + map.m_llen);
+ DBG_BUGON(ptr < map.m_la);
+
+ if (ptr > map.m_la) {
+ map.m_pa += ptr - map.m_la;
+ map.m_la = ptr;
+ }
+
+ ret = dev_read(buffer + ptr - offset,
+ map.m_pa, eend - map.m_la);
+ if (ret < 0)
+ return -EIO;
+
+ ptr = eend;
+ }
+ return 0;
+}
+
+static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
+ erofs_off_t size, erofs_off_t offset)
+{
+ int ret;
+ erofs_off_t end, length, skip;
+ struct erofs_map_blocks map = {
+ .index = UINT_MAX,
+ };
+ bool partial;
+ unsigned int algorithmformat;
+ char raw[EROFS_BLKSIZ];
+
+ end = offset + size;
+ while (end > offset) {
+ map.m_la = end - 1;
+
+ ret = z_erofs_map_blocks_iter(inode, &map);
+ if (ret)
+ return ret;
+
+ if (!(map.m_flags & EROFS_MAP_MAPPED)) {
+ end = map.m_la;
+ continue;
+ }
+
+ ret = dev_read(raw, map.m_pa, EROFS_BLKSIZ);
+ if (ret < 0)
+ return -EIO;
+
+ algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?
+ Z_EROFS_COMPRESSION_LZ4 :
+ Z_EROFS_COMPRESSION_SHIFTED;
+
+ /*
+ * trim to the needed size if the returned extent is quite
+ * larger than requested, and set up partial flag as well.
+ */
+ if (end < map.m_la + map.m_llen) {
+ length = end - map.m_la;
+ partial = true;
+ } else {
+ DBG_BUGON(end != map.m_la + map.m_llen);
+ length = map.m_llen;
+ partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
+ }
+
+ if (map.m_la < offset) {
+ skip = offset - map.m_la;
+ end = offset;
+ } else {
+ skip = 0;
+ end = map.m_la;
+ }
+
+ ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
+ .in = raw,
+ .out = buffer + end - offset,
+ .decodedskip = skip,
+ .inputsize = map.m_plen,
+ .decodedlength = length,
+ .alg = algorithmformat,
+ .partial_decoding = partial
+ });
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+int erofs_pread(struct erofs_inode *inode, char *buf,
+ erofs_off_t count, erofs_off_t offset)
+{
+ switch (inode->datalayout) {
+ case EROFS_INODE_FLAT_PLAIN:
+ case EROFS_INODE_FLAT_INLINE:
+ return erofs_read_raw_data(inode, buf, count, offset);
+ case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
+ case EROFS_INODE_FLAT_COMPRESSION:
+ return z_erofs_read_data(inode, buf, count, offset);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
diff --git a/lib/decompress.c b/lib/decompress.c
new file mode 100644
index 0000000..490c4bc
--- /dev/null
+++ b/lib/decompress.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/lib/decompress.c
+ *
+ * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
+ * Created by Huang Jianan <huangjianan@oppo.com>
+ */
+#include <stdlib.h>
+
+#include "erofs/decompress.h"
+#include "erofs/err.h"
+
+#ifdef LZ4_ENABLED
+#include <lz4.h>
+
+static int z_erofs_decompress_lz4(struct z_erofs_decompress_req *rq)
+{
+ int ret = 0;
+ char *dest = rq->out;
+ char *src = rq->in;
+ char *buff = NULL;
+ bool support_0padding = false;
+ unsigned int inputmargin = 0;
+
+ if (erofs_sb_has_lz4_0padding()) {
+ support_0padding = true;
+
+ while (!src[inputmargin & ~PAGE_MASK])
+ if (!(++inputmargin & ~PAGE_MASK))
+ break;
+
+ if (inputmargin >= rq->inputsize)
+ return -EIO;
+ }
+
+ if (rq->decodedskip) {
+ buff = malloc(rq->decodedlength);
+ if (!buff)
+ return -ENOMEM;
+ dest = buff;
+ }
+
+ if (rq->partial_decoding || !support_0padding)
+ ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
+ rq->inputsize - inputmargin,
+ rq->decodedlength, rq->decodedlength);
+ else
+ ret = LZ4_decompress_safe(src + inputmargin, dest,
+ rq->inputsize - inputmargin,
+ rq->decodedlength);
+
+ if (ret != (int)rq->decodedlength) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (rq->decodedskip)
+ memcpy(rq->out, dest + rq->decodedskip,
+ rq->decodedlength - rq->decodedskip);
+
+out:
+ if (buff)
+ free(buff);
+
+ return ret;
+}
+#endif
+
+int z_erofs_decompress(struct z_erofs_decompress_req *rq)
+{
+ if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED) {
+ if (rq->inputsize != EROFS_BLKSIZ)
+ return -EFSCORRUPTED;
+
+ DBG_BUGON(rq->decodedlength > EROFS_BLKSIZ);
+ DBG_BUGON(rq->decodedlength < rq->decodedskip);
+
+ memcpy(rq->out, rq->in + rq->decodedskip,
+ rq->decodedlength - rq->decodedskip);
+ return 0;
+ }
+
+#ifdef LZ4_ENABLED
+ if (rq->alg == Z_EROFS_COMPRESSION_LZ4)
+ return z_erofs_decompress_lz4(rq);
+#endif
+ return -EOPNOTSUPP;
+}
diff --git a/lib/inode.c b/lib/inode.c
index 43c807f..3d634fc 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/inode.c
+ * erofs-utils/lib/inode.c
*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* http://www.huawei.com/
@@ -25,7 +25,7 @@
struct erofs_sb_info sbi;
#define S_SHIFT 12
-static unsigned char erofs_type_by_mode[S_IFMT >> S_SHIFT] = {
+static unsigned char erofs_ftype_by_mode[S_IFMT >> S_SHIFT] = {
[S_IFREG >> S_SHIFT] = EROFS_FT_REG_FILE,
[S_IFDIR >> S_SHIFT] = EROFS_FT_DIR,
[S_IFCHR >> S_SHIFT] = EROFS_FT_CHRDEV,
@@ -35,7 +35,12 @@ static unsigned char erofs_type_by_mode[S_IFMT >> S_SHIFT] = {
[S_IFLNK >> S_SHIFT] = EROFS_FT_SYMLINK,
};
-#define NR_INODE_HASHTABLE 64
+static unsigned char erofs_mode_to_ftype(umode_t mode)
+{
+ return erofs_ftype_by_mode[(mode & S_IFMT) >> S_SHIFT];
+}
+
+#define NR_INODE_HASHTABLE 16384
struct list_head inode_hashtable[NR_INODE_HASHTABLE];
@@ -54,14 +59,14 @@ static struct erofs_inode *erofs_igrab(struct erofs_inode *inode)
}
/* get the inode from the (source) inode # */
-struct erofs_inode *erofs_iget(ino_t ino)
+struct erofs_inode *erofs_iget(dev_t dev, ino_t ino)
{
struct list_head *head =
- &inode_hashtable[ino % NR_INODE_HASHTABLE];
+ &inode_hashtable[(ino ^ dev) % NR_INODE_HASHTABLE];
struct erofs_inode *inode;
list_for_each_entry(inode, head, i_hash)
- if (inode->i_ino[1] == ino)
+ if (inode->i_ino[1] == ino && inode->dev == dev)
return erofs_igrab(inode);
return NULL;
}
@@ -156,7 +161,7 @@ static int __allocate_inode_bh_data(struct erofs_inode *inode,
int erofs_prepare_dir_file(struct erofs_inode *dir)
{
struct erofs_dentry *d;
- unsigned int d_size;
+ unsigned int d_size, i_nlink;
int ret;
/* dot is pointed to the current dir inode */
@@ -169,16 +174,28 @@ int erofs_prepare_dir_file(struct erofs_inode *dir)
d->inode = erofs_igrab(dir->i_parent);
d->type = EROFS_FT_DIR;
- /* let's calculate dir size */
+ /* let's calculate dir size and update i_nlink */
d_size = 0;
+ i_nlink = 0;
list_for_each_entry(d, &dir->i_subdirs, d_child) {
int len = strlen(d->name) + sizeof(struct erofs_dirent);
if (d_size % EROFS_BLKSIZ + len > EROFS_BLKSIZ)
d_size = round_up(d_size, EROFS_BLKSIZ);
d_size += len;
+
+ i_nlink += (d->type == EROFS_FT_DIR);
}
dir->i_size = d_size;
+ /*
+ * if there're too many subdirs as compact form, set nlink=1
+ * rather than upgrade to use extented form instead.
+ */
+ if (i_nlink > USHRT_MAX &&
+ dir->inode_isize == sizeof(struct erofs_inode_compact))
+ dir->i_nlink = 1;
+ else
+ dir->i_nlink = i_nlink;
/* no compression for all dirs */
dir->datalayout = EROFS_INODE_FLAT_INLINE;
@@ -729,8 +746,19 @@ int erofs_fill_inode(struct erofs_inode *inode,
inode->i_mode = st->st_mode;
inode->i_uid = st->st_uid;
inode->i_gid = st->st_gid;
- inode->i_ctime = sbi.build_time;
- inode->i_ctime_nsec = sbi.build_time_nsec;
+ inode->i_ctime = st->st_ctime;
+ inode->i_ctime_nsec = st->st_ctim.tv_nsec;
+
+ switch (cfg.c_timeinherit) {
+ case TIMESTAMP_CLAMPING:
+ if (st->st_ctime < sbi.build_time)
+ break;
+ case TIMESTAMP_FIXED:
+ inode->i_ctime = sbi.build_time;
+ inode->i_ctime_nsec = sbi.build_time_nsec;
+ default:
+ break;
+ }
inode->i_nlink = 1; /* fix up later if needed */
switch (inode->i_mode & S_IFMT) {
@@ -753,6 +781,7 @@ int erofs_fill_inode(struct erofs_inode *inode,
strncpy(inode->i_srcpath, path, sizeof(inode->i_srcpath) - 1);
inode->i_srcpath[sizeof(inode->i_srcpath) - 1] = '\0';
+ inode->dev = st->st_dev;
inode->i_ino[1] = st->st_ino;
if (erofs_should_use_inode_extended(inode)) {
@@ -767,7 +796,8 @@ int erofs_fill_inode(struct erofs_inode *inode,
}
list_add(&inode->i_hash,
- &inode_hashtable[st->st_ino % NR_INODE_HASHTABLE]);
+ &inode_hashtable[(st->st_ino ^ st->st_dev) %
+ NR_INODE_HASHTABLE]);
return 0;
}
@@ -812,9 +842,16 @@ struct erofs_inode *erofs_iget_from_path(const char *path, bool is_src)
if (ret)
return ERR_PTR(-errno);
- inode = erofs_iget(st.st_ino);
- if (inode)
- return inode;
+ /*
+ * lookup in hash table first, if it already exists we have a
+ * hard-link, just return it. Also don't lookup for directories
+ * since hard-link directory isn't allowed.
+ */
+ if (!S_ISDIR(st.st_mode)) {
+ inode = erofs_iget(st.st_dev, st.st_ino);
+ if (inode)
+ return inode;
+ }
/* cannot find in the inode cache */
inode = erofs_new_inode();
@@ -897,7 +934,9 @@ struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
if (ret)
return ERR_PTR(ret);
} else {
- erofs_write_file(dir);
+ ret = erofs_write_file(dir);
+ if (ret)
+ return ERR_PTR(ret);
}
erofs_prepare_inode_buffer(dir);
@@ -935,6 +974,10 @@ struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
ret = PTR_ERR(d);
goto err_closedir;
}
+
+ /* to count i_nlink for directories */
+ d->type = (dp->d_type == DT_DIR ?
+ EROFS_FT_DIR : EROFS_FT_UNKNOWN);
}
if (errno) {
@@ -945,17 +988,18 @@ struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
ret = erofs_prepare_dir_file(dir);
if (ret)
- goto err_closedir;
+ goto err;
ret = erofs_prepare_inode_buffer(dir);
if (ret)
- goto err_closedir;
+ goto err;
if (IS_ROOT(dir))
erofs_fixup_meta_blkaddr(dir);
list_for_each_entry(d, &dir->i_subdirs, d_child) {
char buf[PATH_MAX];
+ unsigned char ftype;
if (is_dot_dotdot(d->name)) {
erofs_d_invalidate(d);
@@ -971,13 +1015,17 @@ struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
d->inode = erofs_mkfs_build_tree_from_path(dir, buf);
if (IS_ERR(d->inode)) {
+ ret = PTR_ERR(d->inode);
fail:
d->inode = NULL;
d->type = EROFS_FT_UNKNOWN;
- continue;
+ goto err;
}
- d->type = erofs_type_by_mode[d->inode->i_mode >> S_SHIFT];
+ ftype = erofs_mode_to_ftype(d->inode->i_mode);
+ DBG_BUGON(ftype == EROFS_FT_DIR && d->type != ftype);
+ d->type = ftype;
+
erofs_d_invalidate(d);
erofs_info("add file %s/%s (nid %llu, type %d)",
dir->i_srcpath, d->name, (unsigned long long)d->nid,
@@ -989,6 +1037,7 @@ fail:
err_closedir:
closedir(_dir);
+err:
return ERR_PTR(ret);
}
diff --git a/lib/io.c b/lib/io.c
index 5b998d8..d835f34 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/io.c
+ * erofs-utils/lib/io.c
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
@@ -108,6 +108,22 @@ int dev_open(const char *dev)
return 0;
}
+/* XXX: temporary soluation. Disk I/O implementation needs to be refactored. */
+int dev_open_ro(const char *dev)
+{
+ int fd = open(dev, O_RDONLY | O_BINARY);
+
+ if (fd < 0) {
+ erofs_err("failed to open(%s).", dev);
+ return -errno;
+ }
+
+ erofs_devfd = fd;
+ erofs_devname = dev;
+ erofs_devsz = INT64_MAX;
+ return 0;
+}
+
u64 dev_length(void)
{
return erofs_devsz;
diff --git a/lib/namei.c b/lib/namei.c
new file mode 100644
index 0000000..4e06ba4
--- /dev/null
+++ b/lib/namei.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/lib/namei.c
+ *
+ * Created by Li Guifu <blucerlee@gmail.com>
+ */
+#include <linux/kdev_t.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include "erofs/print.h"
+#include "erofs/io.h"
+
+static dev_t erofs_new_decode_dev(u32 dev)
+{
+ const unsigned int major = (dev & 0xfff00) >> 8;
+ const unsigned int minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+
+ return makedev(major, minor);
+}
+
+static int erofs_read_inode_from_disk(struct erofs_inode *vi)
+{
+ int ret, ifmt;
+ char buf[sizeof(struct erofs_inode_extended)];
+ struct erofs_inode_compact *dic;
+ struct erofs_inode_extended *die;
+ const erofs_off_t inode_loc = iloc(vi->nid);
+
+ ret = dev_read(buf, inode_loc, sizeof(*dic));
+ if (ret < 0)
+ return -EIO;
+
+ dic = (struct erofs_inode_compact *)buf;
+ ifmt = le16_to_cpu(dic->i_format);
+
+ vi->datalayout = erofs_inode_datalayout(ifmt);
+ if (vi->datalayout >= EROFS_INODE_DATALAYOUT_MAX) {
+ erofs_err("unsupported datalayout %u of nid %llu",
+ vi->datalayout, vi->nid | 0ULL);
+ return -EOPNOTSUPP;
+ }
+ switch (erofs_inode_version(ifmt)) {
+ case EROFS_INODE_LAYOUT_EXTENDED:
+ vi->inode_isize = sizeof(struct erofs_inode_extended);
+
+ ret = dev_read(buf + sizeof(*dic), inode_loc + sizeof(*dic),
+ sizeof(*die) - sizeof(*dic));
+ if (ret < 0)
+ return -EIO;
+
+ die = (struct erofs_inode_extended *)buf;
+ vi->xattr_isize = erofs_xattr_ibody_size(die->i_xattr_icount);
+ vi->i_mode = le16_to_cpu(die->i_mode);
+
+ switch (vi->i_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ vi->u.i_blkaddr = le32_to_cpu(die->i_u.raw_blkaddr);
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ vi->u.i_rdev =
+ erofs_new_decode_dev(le32_to_cpu(die->i_u.rdev));
+ break;
+ case S_IFIFO:
+ case S_IFSOCK:
+ vi->u.i_rdev = 0;
+ break;
+ default:
+ goto bogusimode;
+ }
+
+ vi->i_uid = le32_to_cpu(die->i_uid);
+ vi->i_gid = le32_to_cpu(die->i_gid);
+ vi->i_nlink = le32_to_cpu(die->i_nlink);
+
+ vi->i_ctime = le64_to_cpu(die->i_ctime);
+ vi->i_ctime_nsec = le64_to_cpu(die->i_ctime_nsec);
+ vi->i_size = le64_to_cpu(die->i_size);
+ break;
+ case EROFS_INODE_LAYOUT_COMPACT:
+ vi->inode_isize = sizeof(struct erofs_inode_compact);
+ vi->xattr_isize = erofs_xattr_ibody_size(dic->i_xattr_icount);
+ vi->i_mode = le16_to_cpu(dic->i_mode);
+
+ switch (vi->i_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ vi->u.i_blkaddr = le32_to_cpu(dic->i_u.raw_blkaddr);
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ vi->u.i_rdev =
+ erofs_new_decode_dev(le32_to_cpu(dic->i_u.rdev));
+ break;
+ case S_IFIFO:
+ case S_IFSOCK:
+ vi->u.i_rdev = 0;
+ break;
+ default:
+ goto bogusimode;
+ }
+
+ vi->i_uid = le16_to_cpu(dic->i_uid);
+ vi->i_gid = le16_to_cpu(dic->i_gid);
+ vi->i_nlink = le16_to_cpu(dic->i_nlink);
+
+ vi->i_ctime = sbi.build_time;
+ vi->i_ctime_nsec = sbi.build_time_nsec;
+
+ vi->i_size = le32_to_cpu(dic->i_size);
+ break;
+ default:
+ erofs_err("unsupported on-disk inode version %u of nid %llu",
+ erofs_inode_version(ifmt), vi->nid | 0ULL);
+ return -EOPNOTSUPP;
+ }
+
+ vi->flags = 0;
+ if (erofs_inode_is_data_compressed(vi->datalayout))
+ z_erofs_fill_inode(vi);
+ return 0;
+bogusimode:
+ erofs_err("bogus i_mode (%o) @ nid %llu", vi->i_mode, vi->nid | 0ULL);
+ return -EFSCORRUPTED;
+}
+
+
+struct erofs_dirent *find_target_dirent(erofs_nid_t pnid,
+ void *dentry_blk,
+ const char *name, unsigned int len,
+ unsigned int nameoff,
+ unsigned int maxsize)
+{
+ struct erofs_dirent *de = dentry_blk;
+ const struct erofs_dirent *end = dentry_blk + nameoff;
+
+ while (de < end) {
+ const char *de_name;
+ unsigned int de_namelen;
+
+ nameoff = le16_to_cpu(de->nameoff);
+ de_name = (char *)dentry_blk + nameoff;
+
+ /* the last dirent in the block? */
+ if (de + 1 >= end)
+ de_namelen = strnlen(de_name, maxsize - nameoff);
+ else
+ de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
+
+ /* a corrupted entry is found */
+ if (nameoff + de_namelen > maxsize ||
+ de_namelen > EROFS_NAME_LEN) {
+ erofs_err("bogus dirent @ nid %llu", pnid | 0ULL);
+ DBG_BUGON(1);
+ return ERR_PTR(-EFSCORRUPTED);
+ }
+
+ if (len == de_namelen && !memcmp(de_name, name, de_namelen))
+ return de;
+ ++de;
+ }
+ return NULL;
+}
+
+struct nameidata {
+ erofs_nid_t nid;
+ unsigned int ftype;
+};
+
+int erofs_namei(struct nameidata *nd,
+ const char *name, unsigned int len)
+{
+ erofs_nid_t nid = nd->nid;
+ int ret;
+ char buf[EROFS_BLKSIZ];
+ struct erofs_inode vi = { .nid = nid };
+ erofs_off_t offset;
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret)
+ return ret;
+
+ offset = 0;
+ while (offset < vi.i_size) {
+ erofs_off_t maxsize = min_t(erofs_off_t,
+ vi.i_size - offset, EROFS_BLKSIZ);
+ struct erofs_dirent *de = (void *)buf;
+ unsigned int nameoff;
+
+ ret = erofs_pread(&vi, buf, maxsize, offset);
+ if (ret)
+ return ret;
+
+ nameoff = le16_to_cpu(de->nameoff);
+ if (nameoff < sizeof(struct erofs_dirent) ||
+ nameoff >= PAGE_SIZE) {
+ erofs_err("invalid de[0].nameoff %u @ nid %llu",
+ nameoff, nid | 0ULL);
+ return -EFSCORRUPTED;
+ }
+
+ de = find_target_dirent(nid, buf, name, len,
+ nameoff, maxsize);
+ if (IS_ERR(de))
+ return PTR_ERR(de);
+
+ if (de) {
+ nd->nid = le64_to_cpu(de->nid);
+ return 0;
+ }
+ offset += maxsize;
+ }
+ return -ENOENT;
+}
+
+static int link_path_walk(const char *name, struct nameidata *nd)
+{
+ nd->nid = sbi.root_nid;
+
+ while (*name == '/')
+ name++;
+
+ /* At this point we know we have a real path component. */
+ while (*name != '\0') {
+ const char *p = name;
+ int ret;
+
+ do {
+ ++p;
+ } while (*p != '\0' && *p != '/');
+
+ DBG_BUGON(p <= name);
+ ret = erofs_namei(nd, name, p - name);
+ if (ret)
+ return ret;
+
+ name = p;
+ /* Skip until no more slashes. */
+ for (name = p; *name == '/'; ++name);
+ }
+ return 0;
+}
+
+int erofs_ilookup(const char *path, struct erofs_inode *vi)
+{
+ int ret;
+ struct nameidata nd;
+
+ ret = link_path_walk(path, &nd);
+ if (ret)
+ return ret;
+
+ vi->nid = nd.nid;
+ return erofs_read_inode_from_disk(vi);
+}
+
diff --git a/lib/super.c b/lib/super.c
new file mode 100644
index 0000000..2d36692
--- /dev/null
+++ b/lib/super.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/lib/super.c
+ *
+ * Created by Li Guifu <blucerlee@gmail.com>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <asm-generic/errno-base.h>
+
+#include "erofs/io.h"
+#include "erofs/print.h"
+
+struct erofs_sb_info sbi;
+
+static bool check_layout_compatibility(struct erofs_sb_info *sbi,
+ struct erofs_super_block *dsb)
+{
+ const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
+
+ sbi->feature_incompat = feature;
+
+ /* check if current kernel meets all mandatory requirements */
+ if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {
+ erofs_err("unidentified incompatible feature %x, please upgrade kernel version",
+ feature & ~EROFS_ALL_FEATURE_INCOMPAT);
+ return false;
+ }
+ return true;
+}
+
+int erofs_read_superblock(void)
+{
+ char data[EROFS_BLKSIZ];
+ struct erofs_super_block *dsb;
+ unsigned int blkszbits;
+ int ret;
+
+ ret = blk_read(data, 0, 1);
+ if (ret < 0) {
+ erofs_err("cannot read erofs superblock: %d", ret);
+ return -EIO;
+ }
+ dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET);
+
+ ret = -EINVAL;
+ if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) {
+ erofs_err("cannot find valid erofs superblock");
+ return ret;
+ }
+
+ sbi.feature_compat = le32_to_cpu(dsb->feature_compat);
+
+ blkszbits = dsb->blkszbits;
+ /* 9(512 bytes) + LOG_SECTORS_PER_BLOCK == LOG_BLOCK_SIZE */
+ if (blkszbits != LOG_BLOCK_SIZE) {
+ erofs_err("blksize %u isn't supported on this platform",
+ 1 << blkszbits);
+ return ret;
+ }
+
+ if (!check_layout_compatibility(&sbi, dsb))
+ return ret;
+
+ sbi.blocks = le32_to_cpu(dsb->blocks);
+ sbi.meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
+ sbi.xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
+ sbi.islotbits = EROFS_ISLOTBITS;
+ sbi.root_nid = le16_to_cpu(dsb->root_nid);
+ sbi.inos = le64_to_cpu(dsb->inos);
+
+ sbi.build_time = le64_to_cpu(dsb->build_time);
+ sbi.build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
+
+ memcpy(&sbi.uuid, dsb->uuid, sizeof(dsb->uuid));
+ return 0;
+}
+
diff --git a/lib/xattr.c b/lib/xattr.c
index b9ac223..49ebb9c 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs_utils/lib/xattr.c
+ * erofs-utils/lib/xattr.c
*
* Originally contributed by an anonymous person,
* heavily changed by Li Guifu <blucerlee@gmail.com>
@@ -18,6 +18,7 @@
#include "erofs/hashtable.h"
#include "erofs/xattr.h"
#include "erofs/cache.h"
+#include "erofs/io.h"
#define EA_HASHTABLE_BITS 16
@@ -506,8 +507,9 @@ static void erofs_cleanxattrs(bool sharedxattrs)
{
unsigned int i;
struct xattr_item *item;
+ struct hlist_node *tmp;
- hash_for_each(ea_hashtable, i, item, node) {
+ hash_for_each_safe(ea_hashtable, i, tmp, item, node) {
if (sharedxattrs && item->shared_xattr_id >= 0)
continue;
@@ -521,6 +523,21 @@ static void erofs_cleanxattrs(bool sharedxattrs)
shared_xattrs_size = shared_xattrs_count = 0;
}
+static bool erofs_bh_flush_write_shared_xattrs(struct erofs_buffer_head *bh)
+{
+ void *buf = bh->fsprivate;
+ int err = dev_write(buf, erofs_btell(bh, false), shared_xattrs_size);
+
+ if (err)
+ return false;
+ free(buf);
+ return erofs_bh_flush_generic_end(bh);
+}
+
+static struct erofs_bhops erofs_write_shared_xattrs_bhops = {
+ .flush = erofs_bh_flush_write_shared_xattrs,
+};
+
int erofs_build_shared_xattrs_from_path(const char *path)
{
int ret;
@@ -547,7 +564,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
if (!shared_xattrs_size)
goto out;
- buf = malloc(shared_xattrs_size);
+ buf = calloc(1, shared_xattrs_size);
if (!buf)
return -ENOMEM;
@@ -585,7 +602,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
free(node);
}
bh->fsprivate = buf;
- bh->op = &erofs_buf_write_bhops;
+ bh->op = &erofs_write_shared_xattrs_bhops;
out:
erofs_cleanxattrs(true);
return 0;
diff --git a/lib/zmap.c b/lib/zmap.c
new file mode 100644
index 0000000..ee63de7
--- /dev/null
+++ b/lib/zmap.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-utils/lib/zmap.c
+ *
+ * (a large amount of code was adapted from Linux kernel. )
+ *
+ * Copyright (C) 2018-2019 HUAWEI, Inc.
+ * https://www.huawei.com/
+ * Created by Gao Xiang <gaoxiang25@huawei.com>
+ * Modified by Huang Jianan <huangjianan@oppo.com>
+ */
+#include "erofs/io.h"
+#include "erofs/print.h"
+
+int z_erofs_fill_inode(struct erofs_inode *vi)
+{
+ if (vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY) {
+ vi->z_advise = 0;
+ vi->z_algorithmtype[0] = 0;
+ vi->z_algorithmtype[1] = 0;
+ vi->z_logical_clusterbits = LOG_BLOCK_SIZE;
+ vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits;
+ vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits;
+
+ vi->flags |= EROFS_I_Z_INITED;
+ }
+ return 0;
+}
+
+static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
+{
+ int ret;
+ erofs_off_t pos;
+ struct z_erofs_map_header *h;
+ char buf[sizeof(struct z_erofs_map_header)];
+
+ if (vi->flags & EROFS_I_Z_INITED)
+ return 0;
+
+ DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
+ pos = round_up(iloc(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
+
+ ret = dev_read(buf, pos, sizeof(buf));
+ if (ret < 0)
+ return -EIO;
+
+ h = (struct z_erofs_map_header *)buf;
+ vi->z_advise = le16_to_cpu(h->h_advise);
+ vi->z_algorithmtype[0] = h->h_algorithmtype & 15;
+ vi->z_algorithmtype[1] = h->h_algorithmtype >> 4;
+
+ if (vi->z_algorithmtype[0] >= Z_EROFS_COMPRESSION_MAX) {
+ erofs_err("unknown compression format %u for nid %llu",
+ vi->z_algorithmtype[0], (unsigned long long)vi->nid);
+ return -EOPNOTSUPP;
+ }
+
+ vi->z_logical_clusterbits = LOG_BLOCK_SIZE + (h->h_clusterbits & 7);
+ vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits +
+ ((h->h_clusterbits >> 3) & 3);
+
+ if (vi->z_physical_clusterbits[0] != LOG_BLOCK_SIZE) {
+ erofs_err("unsupported physical clusterbits %u for nid %llu",
+ vi->z_physical_clusterbits[0], (unsigned long long)vi->nid);
+ return -EOPNOTSUPP;
+ }
+
+ vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits +
+ ((h->h_clusterbits >> 5) & 7);
+ vi->flags |= EROFS_I_Z_INITED;
+ return 0;
+}
+
+struct z_erofs_maprecorder {
+ struct erofs_inode *inode;
+ struct erofs_map_blocks *map;
+ void *kaddr;
+
+ unsigned long lcn;
+ /* compression extent information gathered */
+ u8 type;
+ u16 clusterofs;
+ u16 delta[2];
+ erofs_blk_t pblk;
+};
+
+static int z_erofs_reload_indexes(struct z_erofs_maprecorder *m,
+ erofs_blk_t eblk)
+{
+ int ret;
+ struct erofs_map_blocks *const map = m->map;
+ char *mpage = map->mpage;
+
+ if (map->index == eblk)
+ return 0;
+
+ ret = blk_read(mpage, eblk, 1);
+ if (ret < 0)
+ return -EIO;
+
+ map->index = eblk;
+
+ return 0;
+}
+
+static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,
+ unsigned long lcn)
+{
+ struct erofs_inode *const vi = m->inode;
+ const erofs_off_t ibase = iloc(vi->nid);
+ const erofs_off_t pos =
+ Z_EROFS_VLE_LEGACY_INDEX_ALIGN(ibase + vi->inode_isize +
+ vi->xattr_isize) +
+ lcn * sizeof(struct z_erofs_vle_decompressed_index);
+ struct z_erofs_vle_decompressed_index *di;
+ unsigned int advise, type;
+ int err;
+
+ err = z_erofs_reload_indexes(m, erofs_blknr(pos));
+ if (err)
+ return err;
+
+ m->lcn = lcn;
+ di = m->kaddr + erofs_blkoff(pos);
+
+ advise = le16_to_cpu(di->di_advise);
+ type = (advise >> Z_EROFS_VLE_DI_CLUSTER_TYPE_BIT) &
+ ((1 << Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) - 1);
+ switch (type) {
+ case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
+ m->clusterofs = 1 << vi->z_logical_clusterbits;
+ m->delta[0] = le16_to_cpu(di->di_u.delta[0]);
+ m->delta[1] = le16_to_cpu(di->di_u.delta[1]);
+ break;
+ case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
+ case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
+ m->clusterofs = le16_to_cpu(di->di_clusterofs);
+ m->pblk = le32_to_cpu(di->di_u.blkaddr);
+ break;
+ default:
+ DBG_BUGON(1);
+ return -EOPNOTSUPP;
+ }
+ m->type = type;
+ return 0;
+}
+
+static unsigned int decode_compactedbits(unsigned int lobits,
+ unsigned int lomask,
+ u8 *in, unsigned int pos, u8 *type)
+{
+ const unsigned int v = get_unaligned_le32(in + pos / 8) >> (pos & 7);
+ const unsigned int lo = v & lomask;
+
+ *type = (v >> lobits) & 3;
+ return lo;
+}
+
+static int unpack_compacted_index(struct z_erofs_maprecorder *m,
+ unsigned int amortizedshift,
+ unsigned int eofs)
+{
+ struct erofs_inode *const vi = m->inode;
+ const unsigned int lclusterbits = vi->z_logical_clusterbits;
+ const unsigned int lomask = (1 << lclusterbits) - 1;
+ unsigned int vcnt, base, lo, encodebits, nblk;
+ int i;
+ u8 *in, type;
+
+ if (1 << amortizedshift == 4)
+ vcnt = 2;
+ else if (1 << amortizedshift == 2 && lclusterbits == 12)
+ vcnt = 16;
+ else
+ return -EOPNOTSUPP;
+
+ encodebits = ((vcnt << amortizedshift) - sizeof(__le32)) * 8 / vcnt;
+ base = round_down(eofs, vcnt << amortizedshift);
+ in = m->kaddr + base;
+
+ i = (eofs - base) >> amortizedshift;
+
+ lo = decode_compactedbits(lclusterbits, lomask,
+ in, encodebits * i, &type);
+ m->type = type;
+ if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
+ m->clusterofs = 1 << lclusterbits;
+ if (i + 1 != (int)vcnt) {
+ m->delta[0] = lo;
+ return 0;
+ }
+ /*
+ * since the last lcluster in the pack is special,
+ * of which lo saves delta[1] rather than delta[0].
+ * Hence, get delta[0] by the previous lcluster indirectly.
+ */
+ lo = decode_compactedbits(lclusterbits, lomask,
+ in, encodebits * (i - 1), &type);
+ if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)
+ lo = 0;
+ m->delta[0] = lo + 1;
+ return 0;
+ }
+ m->clusterofs = lo;
+ m->delta[0] = 0;
+ /* figout out blkaddr (pblk) for HEAD lclusters */
+ nblk = 1;
+ while (i > 0) {
+ --i;
+ lo = decode_compactedbits(lclusterbits, lomask,
+ in, encodebits * i, &type);
+ if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)
+ i -= lo;
+
+ if (i >= 0)
+ ++nblk;
+ }
+ in += (vcnt << amortizedshift) - sizeof(__le32);
+ m->pblk = le32_to_cpu(*(__le32 *)in) + nblk;
+ return 0;
+}
+
+static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,
+ unsigned long lcn)
+{
+ struct erofs_inode *const vi = m->inode;
+ const unsigned int lclusterbits = vi->z_logical_clusterbits;
+ const erofs_off_t ebase = round_up(iloc(vi->nid) + vi->inode_isize +
+ vi->xattr_isize, 8) +
+ sizeof(struct z_erofs_map_header);
+ const unsigned int totalidx = DIV_ROUND_UP(vi->i_size, EROFS_BLKSIZ);
+ unsigned int compacted_4b_initial, compacted_2b;
+ unsigned int amortizedshift;
+ erofs_off_t pos;
+ int err;
+
+ if (lclusterbits != 12)
+ return -EOPNOTSUPP;
+
+ if (lcn >= totalidx)
+ return -EINVAL;
+
+ m->lcn = lcn;
+ /* used to align to 32-byte (compacted_2b) alignment */
+ compacted_4b_initial = (32 - ebase % 32) / 4;
+ if (compacted_4b_initial == 32 / 4)
+ compacted_4b_initial = 0;
+
+ if (vi->z_advise & Z_EROFS_ADVISE_COMPACTED_2B)
+ compacted_2b = rounddown(totalidx - compacted_4b_initial, 16);
+ else
+ compacted_2b = 0;
+
+ pos = ebase;
+ if (lcn < compacted_4b_initial) {
+ amortizedshift = 2;
+ goto out;
+ }
+ pos += compacted_4b_initial * 4;
+ lcn -= compacted_4b_initial;
+
+ if (lcn < compacted_2b) {
+ amortizedshift = 1;
+ goto out;
+ }
+ pos += compacted_2b * 2;
+ lcn -= compacted_2b;
+ amortizedshift = 2;
+out:
+ pos += lcn * (1 << amortizedshift);
+ err = z_erofs_reload_indexes(m, erofs_blknr(pos));
+ if (err)
+ return err;
+ return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos));
+}
+
+static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
+ unsigned int lcn)
+{
+ const unsigned int datamode = m->inode->datalayout;
+
+ if (datamode == EROFS_INODE_FLAT_COMPRESSION_LEGACY)
+ return legacy_load_cluster_from_disk(m, lcn);
+
+ if (datamode == EROFS_INODE_FLAT_COMPRESSION)
+ return compacted_load_cluster_from_disk(m, lcn);
+
+ return -EINVAL;
+}
+
+static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
+ unsigned int lookback_distance)
+{
+ struct erofs_inode *const vi = m->inode;
+ struct erofs_map_blocks *const map = m->map;
+ const unsigned int lclusterbits = vi->z_logical_clusterbits;
+ unsigned long lcn = m->lcn;
+ int err;
+
+ if (lcn < lookback_distance) {
+ erofs_err("bogus lookback distance @ nid %llu",
+ (unsigned long long)vi->nid);
+ DBG_BUGON(1);
+ return -EFSCORRUPTED;
+ }
+
+ /* load extent head logical cluster if needed */
+ lcn -= lookback_distance;
+ err = z_erofs_load_cluster_from_disk(m, lcn);
+ if (err)
+ return err;
+
+ switch (m->type) {
+ case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
+ if (!m->delta[0]) {
+ erofs_err("invalid lookback distance 0 @ nid %llu",
+ (unsigned long long)vi->nid);
+ DBG_BUGON(1);
+ return -EFSCORRUPTED;
+ }
+ return z_erofs_extent_lookback(m, m->delta[0]);
+ case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
+ map->m_flags &= ~EROFS_MAP_ZIPPED;
+ case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
+ map->m_la = (lcn << lclusterbits) | m->clusterofs;
+ break;
+ default:
+ erofs_err("unknown type %u @ lcn %lu of nid %llu",
+ m->type, lcn, (unsigned long long)vi->nid);
+ DBG_BUGON(1);
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+int z_erofs_map_blocks_iter(struct erofs_inode *vi,
+ struct erofs_map_blocks *map)
+{
+ struct z_erofs_maprecorder m = {
+ .inode = vi,
+ .map = map,
+ .kaddr = map->mpage,
+ };
+ int err = 0;
+ unsigned int lclusterbits, endoff;
+ unsigned long long ofs, end;
+
+ /* when trying to read beyond EOF, leave it unmapped */
+ if (map->m_la >= vi->i_size) {
+ map->m_llen = map->m_la + 1 - vi->i_size;
+ map->m_la = vi->i_size;
+ map->m_flags = 0;
+ goto out;
+ }
+
+ err = z_erofs_fill_inode_lazy(vi);
+ if (err)
+ goto out;
+
+ lclusterbits = vi->z_logical_clusterbits;
+ ofs = map->m_la;
+ m.lcn = ofs >> lclusterbits;
+ endoff = ofs & ((1 << lclusterbits) - 1);
+
+ err = z_erofs_load_cluster_from_disk(&m, m.lcn);
+ if (err)
+ goto out;
+
+ map->m_flags = EROFS_MAP_ZIPPED; /* by default, compressed */
+ end = (m.lcn + 1ULL) << lclusterbits;
+ switch (m.type) {
+ case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
+ if (endoff >= m.clusterofs)
+ map->m_flags &= ~EROFS_MAP_ZIPPED;
+ case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
+ if (endoff >= m.clusterofs) {
+ map->m_la = (m.lcn << lclusterbits) | m.clusterofs;
+ break;
+ }
+ /* m.lcn should be >= 1 if endoff < m.clusterofs */
+ if (!m.lcn) {
+ erofs_err("invalid logical cluster 0 at nid %llu",
+ (unsigned long long)vi->nid);
+ err = -EFSCORRUPTED;
+ goto out;
+ }
+ end = (m.lcn << lclusterbits) | m.clusterofs;
+ map->m_flags |= EROFS_MAP_FULL_MAPPED;
+ m.delta[0] = 1;
+ case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
+ /* get the correspoinding first chunk */
+ err = z_erofs_extent_lookback(&m, m.delta[0]);
+ if (err)
+ goto out;
+ break;
+ default:
+ erofs_err("unknown type %u @ offset %llu of nid %llu",
+ m.type, ofs, (unsigned long long)vi->nid);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ map->m_llen = end - map->m_la;
+ map->m_plen = 1 << lclusterbits;
+ map->m_pa = blknr_to_addr(m.pblk);
+ map->m_flags |= EROFS_MAP_MAPPED;
+
+out:
+ erofs_dbg("m_la %" PRIu64 " m_pa %" PRIu64 " m_llen %" PRIu64 " m_plen %" PRIu64 " m_flags 0%o",
+ map->m_la, map->m_pa,
+ map->m_llen, map->m_plen, map->m_flags);
+
+ DBG_BUGON(err < 0 && err != -ENOMEM);
+ return err;
+}
diff --git a/man/mkfs.erofs.1 b/man/mkfs.erofs.1
index 891c5a8..dcaf9d7 100644
--- a/man/mkfs.erofs.1
+++ b/man/mkfs.erofs.1
@@ -52,6 +52,12 @@ Forcely generate extended inodes (64-byte inodes) to output.
Set all files to the given UNIX timestamp. Reproducible builds requires setting
all to a specific one.
.TP
+.BI "\-U " UUID
+Set the universally unique identifier (UUID) of the filesystem to
+.IR UUID .
+The format of the UUID is a series of hex digits separated by hyphens,
+like this: "c1b9d5a2-f162-11cf-9ece-0020afc76f16".
+.TP
.BI "\-\-exclude-path=" path
Ignore file that matches the exact literal path.
You may give multiple `--exclude-path' options.
diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am
index ecc468c..8b8e051 100644
--- a/mkfs/Makefile.am
+++ b/mkfs/Makefile.am
@@ -6,5 +6,5 @@ bin_PROGRAMS = mkfs.erofs
AM_CPPFLAGS = ${libuuid_CFLAGS} ${libselinux_CFLAGS}
mkfs_erofs_SOURCES = main.c
mkfs_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
-mkfs_erofs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${LZ4_LIBS}
+mkfs_erofs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${liblz4_LIBS}
diff --git a/mkfs/main.c b/mkfs/main.c
index 6dda9e3..c63b274 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -66,6 +66,9 @@ static void usage(void)
" -x# set xattr tolerance to # (< 0, disable xattrs; default 2)\n"
" -EX[,...] X=extended options\n"
" -T# set a fixed UNIX timestamp # to all files\n"
+#ifdef HAVE_LIBUUID
+ " -UX use a given filesystem UUID\n"
+#endif
" --exclude-path=X avoid including file X (X = exact literal path)\n"
" --exclude-regex=X avoid including files that match X (X = regular expression)\n"
#ifdef HAVE_LIBSELINUX
@@ -149,7 +152,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
char *endptr;
int opt, i;
- while((opt = getopt_long(argc, argv, "d:x:z:E:T:",
+ while((opt = getopt_long(argc, argv, "d:x:z:E:T:U:",
long_options, NULL)) != -1) {
switch (opt) {
case 'z':
@@ -198,7 +201,16 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
erofs_err("invalid UNIX timestamp %s", optarg);
return -EINVAL;
}
+ cfg.c_timeinherit = TIMESTAMP_FIXED;
+ break;
+#ifdef HAVE_LIBUUID
+ case 'U':
+ if (uuid_parse(optarg, sbi.uuid)) {
+ erofs_err("invalid UUID %s", optarg);
+ return -EINVAL;
+ }
break;
+#endif
case 2:
opt = erofs_parse_exclude_path(optarg, false);
if (opt) {
@@ -367,18 +379,45 @@ static int erofs_mkfs_superblock_csum_set(void)
return 0;
}
-static void erofs_mkfs_generate_uuid(void)
+static void erofs_mkfs_default_options(void)
{
- char uuid_str[37] = "not available";
+ cfg.c_legacy_compress = false;
+ sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
+ sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM;
+ /* generate a default uuid first */
#ifdef HAVE_LIBUUID
do {
uuid_generate(sbi.uuid);
} while (uuid_is_null(sbi.uuid));
-
- uuid_unparse_lower(sbi.uuid, uuid_str);
#endif
- erofs_info("filesystem UUID: %s", uuid_str);
+}
+
+/* https://reproducible-builds.org/specs/source-date-epoch/ for more details */
+int parse_source_date_epoch(void)
+{
+ char *source_date_epoch;
+ unsigned long long epoch = -1ULL;
+ char *endptr;
+
+ source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+ if (!source_date_epoch)
+ return 0;
+
+ epoch = strtoull(source_date_epoch, &endptr, 10);
+ if (epoch == -1ULL || *endptr != '\0') {
+ erofs_err("Environment variable $SOURCE_DATE_EPOCH %s is invalid",
+ source_date_epoch);
+ return -EINVAL;
+ }
+
+ if (cfg.c_force_inodeversion != FORCE_INODE_EXTENDED)
+ erofs_info("SOURCE_DATE_EPOCH is set, forcely generate extended inodes instead");
+
+ cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
+ cfg.c_unix_timestamp = epoch;
+ cfg.c_timeinherit = TIMESTAMP_CLAMPING;
+ return 0;
}
int main(int argc, char **argv)
@@ -390,13 +429,12 @@ int main(int argc, char **argv)
struct stat64 st;
erofs_blk_t nblocks;
struct timeval t;
+ char uuid_str[37] = "not available";
erofs_init_configure();
fprintf(stderr, "%s %s\n", basename(argv[0]), cfg.c_version);
- cfg.c_legacy_compress = false;
- sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
- sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM;
+ erofs_mkfs_default_options();
err = mkfs_parse_options_cfg(argc, argv);
if (err) {
@@ -405,6 +443,12 @@ int main(int argc, char **argv)
return 1;
}
+ err = parse_source_date_epoch();
+ if (err) {
+ usage();
+ return 1;
+ }
+
err = lstat64(cfg.c_src_path, &st);
if (err)
return 1;
@@ -461,7 +505,11 @@ int main(int argc, char **argv)
goto exit;
}
- erofs_mkfs_generate_uuid();
+#ifdef HAVE_LIBUUID
+ uuid_unparse_lower(sbi.uuid, uuid_str);
+#endif
+ erofs_info("filesystem UUID: %s", uuid_str);
+
erofs_inode_manager_init();
err = erofs_build_shared_xattrs_from_path(cfg.c_src_path);