* [PATCH 1/5] erofs-utils: fix the conflict with the master branch
@ 2020-10-15 13:39 Huang Jianan
2020-10-15 13:39 ` [PATCH 2/5] erofs-utils: fix the wrong name length of tail file in the directory Huang Jianan
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Huang Jianan @ 2020-10-15 13:39 UTC (permalink / raw)
To: linux-erofs; +Cc: guoweichao, zhangshiming
The current fuse branch is quite different from the master branch.
So fix the conflict with the master branch to support the upcoming patch.
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Guo Weichao <guoweichao@oppo.com>
---
fuse/init.c | 4 ++--
fuse/namei.c | 16 ++++++++--------
fuse/read.c | 6 +++---
fuse/readir.c | 4 ++--
include/erofs/internal.h | 6 +++---
5 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/fuse/init.c b/fuse/init.c
index 885705f..8198fa7 100644
--- a/fuse/init.c
+++ b/fuse/init.c
@@ -41,7 +41,7 @@ int erofs_init_super(void)
}
sbk->checksum = le32_to_cpu(sb->checksum);
- sbk->features = le32_to_cpu(sb->features);
+ sbk->feature_compat = le32_to_cpu(sb->feature_compat);
sbk->blkszbits = sb->blkszbits;
ASSERT(sbk->blkszbits != 32);
@@ -56,7 +56,7 @@ int erofs_init_super(void)
sbk->root_nid = le16_to_cpu(sb->root_nid);
logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));
- logp("%-15s:0x%X", STR(features), SUPER_MEM(features));
+ logp("%-15s:0x%X", STR(feature_compat), SUPER_MEM(feature_compat));
logp("%-15s:%u", STR(blkszbits), SUPER_MEM(blkszbits));
logp("%-15s:%u", STR(root_nid), SUPER_MEM(root_nid));
logp("%-15s:%ul", STR(inos), SUPER_MEM(inos));
diff --git a/fuse/namei.c b/fuse/namei.c
index ab497e8..ded9207 100644
--- a/fuse/namei.c
+++ b/fuse/namei.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * erofs-fuse\inode.c
+ * erofs-fuse\namei.c
* Created by Li Guifu <blucerlee@gmail.com>
*/
@@ -43,7 +43,7 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
{
int ret;
char buf[EROFS_BLKSIZ];
- struct erofs_inode_v1 *v1;
+ struct erofs_inode_compact *v1;
const erofs_off_t addr = nid2addr(nid);
const size_t size = EROFS_BLKSIZ - erofs_blkoff(addr);
@@ -51,10 +51,10 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
if (ret != (int)size)
return -EIO;
- v1 = (struct erofs_inode_v1 *)buf;
- vi->data_mapping_mode = __inode_data_mapping(le16_to_cpu(v1->i_advise));
- vi->inode_isize = sizeof(struct erofs_inode_v1);
- vi->xattr_isize = ondisk_xattr_ibody_size(v1->i_xattr_icount);
+ v1 = (struct erofs_inode_compact *)buf;
+ vi->datalayout = __inode_data_mapping(le16_to_cpu(v1->i_format));
+ vi->inode_isize = sizeof(struct erofs_inode_compact);
+ vi->xattr_isize = erofs_xattr_ibody_size(v1->i_xattr_icount);
vi->i_size = le32_to_cpu(v1->i_size);
vi->i_mode = le16_to_cpu(v1->i_mode);
vi->i_uid = le16_to_cpu(v1->i_uid);
@@ -153,10 +153,10 @@ struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
++nr_cnt;
}
- if (v.data_mapping_mode == EROFS_INODE_FLAT_INLINE) {
+ if (v.datalayout == EROFS_INODE_FLAT_INLINE) {
uint32_t dir_off = erofs_blkoff(dirsize);
off_t dir_addr = nid2addr(dcache_get_nid(parent))
- + sizeof(struct erofs_inode_v1);
+ + sizeof(struct erofs_inode_compact);
memset(buf, 0, sizeof(buf));
ret = dev_read(buf, dir_off, dir_addr);
diff --git a/fuse/read.c b/fuse/read.c
index b2bfbd3..ffe976e 100644
--- a/fuse/read.c
+++ b/fuse/read.c
@@ -64,7 +64,7 @@ size_t erofs_read_data_inline(struct erofs_vnode *vnode, char *buffer,
if (!suminline)
goto finished;
- addr = nid2addr(vnode->nid) + sizeof(struct erofs_inode_v1)
+ addr = nid2addr(vnode->nid) + sizeof(struct erofs_inode_compact)
+ vnode->xattr_isize;
ret = dev_read(buffer + rdsz, suminline, addr);
if (ret < 0 || (size_t)ret != suminline)
@@ -97,8 +97,8 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
if (ret)
return ret;
- logi("path:%s nid=%llu mode=%u", path, nid, v.data_mapping_mode);
- switch (v.data_mapping_mode) {
+ logi("path:%s nid=%llu mode=%u", path, nid, v.datalayout);
+ switch (v.datalayout) {
case EROFS_INODE_FLAT_PLAIN:
return erofs_read_data(&v, buffer, size, offset);
diff --git a/fuse/readir.c b/fuse/readir.c
index 7fc69f4..367f935 100644
--- a/fuse/readir.c
+++ b/fuse/readir.c
@@ -103,10 +103,10 @@ int erofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
++nr_cnt;
}
- if (v.data_mapping_mode == EROFS_INODE_FLAT_INLINE) {
+ if (v.datalayout == EROFS_INODE_FLAT_INLINE) {
off_t addr;
- addr = nid2addr(nid) + sizeof(struct erofs_inode_v1)
+ addr = nid2addr(nid) + sizeof(struct erofs_inode_compact)
+ v.xattr_isize;
memset(dirsbuf, 0, sizeof(dirsbuf));
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 2316d01..cba3ce4 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -135,7 +135,7 @@ struct erofs_inode {
};
struct erofs_vnode {
- uint8_t data_mapping_mode;
+ uint8_t datalayout;
uint32_t i_size;
/* inline size in bytes */
@@ -171,8 +171,8 @@ static inline bool is_inode_layout_compression(struct erofs_inode *inode)
EROFS_I_VERSION_BITS)
#define __inode_data_mapping(advise) \
- __inode_advise(advise, EROFS_I_DATA_MAPPING_BIT,\
- EROFS_I_DATA_MAPPING_BITS)
+ __inode_advise(advise, EROFS_I_DATALAYOUT_BIT,\
+ EROFS_I_DATALAYOUT_BITS)
#define IS_ROOT(x) ((x) == (x)->i_parent)
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/5] erofs-utils: fix the wrong name length of tail file in the directory
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
@ 2020-10-15 13:39 ` Huang Jianan
2020-10-15 13:39 ` [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content Huang Jianan
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Huang Jianan @ 2020-10-15 13:39 UTC (permalink / raw)
To: linux-erofs; +Cc: guoweichao, zhangshiming
The file name doesn't end with '\0' if it just fill a block, so we shouldn't use strlen to get file name length, fix it.
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Guo Weichao <guoweichao@oppo.com>
---
fuse/namei.c | 10 ++++++----
fuse/readir.c | 14 ++++++++------
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/fuse/namei.c b/fuse/namei.c
index ded9207..21e6ba0 100644
--- a/fuse/namei.c
+++ b/fuse/namei.c
@@ -87,7 +87,8 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
/* dirent + name string */
struct dcache_entry *list_name(const char *buf, struct dcache_entry *parent,
- const char *name, unsigned int len)
+ const char *name, unsigned int len,
+ uint32_t dirend)
{
struct dcache_entry *entry = NULL;
struct erofs_dirent *ds, *de;
@@ -99,7 +100,8 @@ struct dcache_entry *list_name(const char *buf, struct dcache_entry *parent,
erofs_nid_t nid = le64_to_cpu(ds->nid);
uint16_t nameoff = le16_to_cpu(ds->nameoff);
char *d_name = (char *)(buf + nameoff);
- uint16_t name_len = (ds + 1 >= de) ? (uint16_t)strlen(d_name) :
+ uint16_t name_len = (ds + 1 >= de) ?
+ (uint16_t)strnlen(d_name, dirend - nameoff) :
le16_to_cpu(ds[1].nameoff) - nameoff;
#if defined(EROFS_DEBUG_ENTRY)
@@ -146,7 +148,7 @@ struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
if (dev_read_blk(buf, blkno + nr_cnt) != EROFS_BLKSIZ)
return NULL;
- entry = list_name(buf, parent, name, name_len);
+ entry = list_name(buf, parent, name, name_len, EROFS_BLKSIZ);
if (entry)
goto next;
@@ -163,7 +165,7 @@ struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
if (ret < 0 && (uint32_t)ret != dir_off)
return NULL;
- entry = list_name(buf, parent, name, name_len);
+ entry = list_name(buf, parent, name, name_len, dir_off);
}
next:
return entry;
diff --git a/fuse/readir.c b/fuse/readir.c
index 367f935..9589685 100644
--- a/fuse/readir.c
+++ b/fuse/readir.c
@@ -17,7 +17,8 @@
#include "logging.h"
#include "init.h"
-erofs_nid_t split_entry(char *entry, off_t ofs, char *end, char *name)
+erofs_nid_t split_entry(char *entry, off_t ofs, char *end, char *name,
+ uint32_t dirend)
{
struct erofs_dirent *de = (struct erofs_dirent *)(entry + ofs);
uint16_t nameoff = le16_to_cpu(de->nameoff);
@@ -25,7 +26,7 @@ erofs_nid_t split_entry(char *entry, off_t ofs, char *end, char *name)
uint32_t de_namelen;
if ((void *)(de + 1) >= (void *)end)
- de_namelen = strlen(de_name);
+ de_namelen = strnlen(de_name, dirend - nameoff);
else
de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
@@ -34,7 +35,8 @@ erofs_nid_t split_entry(char *entry, off_t ofs, char *end, char *name)
return le64_to_cpu(de->nid);
}
-int fill_dir(char *entrybuf, fuse_fill_dir_t filler, void *buf)
+int fill_dir(char *entrybuf, fuse_fill_dir_t filler, void *buf,
+ uint32_t dirend)
{
uint32_t ofs;
uint16_t nameoff;
@@ -45,7 +47,7 @@ int fill_dir(char *entrybuf, fuse_fill_dir_t filler, void *buf)
end = entrybuf + nameoff;
ofs = 0;
while (ofs < nameoff) {
- (void)split_entry(entrybuf, ofs, end, name);
+ (void)split_entry(entrybuf, ofs, end, name, dirend);
filler(buf, name, NULL, 0);
ofs += sizeof(struct erofs_dirent);
}
@@ -99,7 +101,7 @@ int erofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
ret = dev_read_blk(dirsbuf, v.raw_blkaddr + nr_cnt);
if (ret != EROFS_BLKSIZ)
return -EIO;
- fill_dir(dirsbuf, filler, buf);
+ fill_dir(dirsbuf, filler, buf, EROFS_BLKSIZ);
++nr_cnt;
}
@@ -113,7 +115,7 @@ int erofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
ret = dev_read(dirsbuf, dir_off, addr);
if (ret < 0 || (uint32_t)ret != dir_off)
return -EIO;
- fill_dir(dirsbuf, filler, buf);
+ fill_dir(dirsbuf, filler, buf, dir_off);
}
return 0;
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content.
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
2020-10-15 13:39 ` [PATCH 2/5] erofs-utils: fix the wrong name length of tail file in the directory Huang Jianan
@ 2020-10-15 13:39 ` Huang Jianan
2020-10-16 15:36 ` Gao Xiang via Linux-erofs
2020-10-15 13:39 ` [PATCH 4/5] erofs-utils: support read special file Huang Jianan
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Huang Jianan @ 2020-10-15 13:39 UTC (permalink / raw)
To: linux-erofs; +Cc: guoweichao, zhangshiming
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Guo Weichao <guoweichao@oppo.com>
---
fuse/namei.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fuse/namei.c b/fuse/namei.c
index 21e6ba0..3503a8d 100644
--- a/fuse/namei.c
+++ b/fuse/namei.c
@@ -158,7 +158,7 @@ struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
if (v.datalayout == EROFS_INODE_FLAT_INLINE) {
uint32_t dir_off = erofs_blkoff(dirsize);
off_t dir_addr = nid2addr(dcache_get_nid(parent))
- + sizeof(struct erofs_inode_compact);
+ + sizeof(struct erofs_inode_compact) + v.xattr_isize;
memset(buf, 0, sizeof(buf));
ret = dev_read(buf, dir_off, dir_addr);
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/5] erofs-utils: support read special file
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
2020-10-15 13:39 ` [PATCH 2/5] erofs-utils: fix the wrong name length of tail file in the directory Huang Jianan
2020-10-15 13:39 ` [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content Huang Jianan
@ 2020-10-15 13:39 ` Huang Jianan
2020-10-15 13:39 ` [PATCH 5/5] erofs-utils: support read compressed file Huang Jianan
2020-10-16 15:41 ` [PATCH 1/5] erofs-utils: fix the conflict with the master branch Gao Xiang via Linux-erofs
4 siblings, 0 replies; 10+ messages in thread
From: Huang Jianan @ 2020-10-15 13:39 UTC (permalink / raw)
To: linux-erofs; +Cc: guoweichao, zhangshiming
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Guo Weichao <guoweichao@oppo.com>
---
fuse/getattr.c | 1 +
fuse/main.c | 1 +
fuse/namei.c | 14 ++++++++++----
fuse/read.c | 26 ++++++++++++++++++++++++++
fuse/read.h | 1 +
include/erofs/internal.h | 1 +
6 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/fuse/getattr.c b/fuse/getattr.c
index 542cf35..d2134f4 100644
--- a/fuse/getattr.c
+++ b/fuse/getattr.c
@@ -56,6 +56,7 @@ int erofs_getattr(const char *path, struct stat *stbuf)
stbuf->st_blocks = stbuf->st_size / EROFS_BLKSIZ;
stbuf->st_uid = le16_to_cpu(v.i_uid);
stbuf->st_gid = le16_to_cpu(v.i_gid);
+ stbuf->st_rdev = le32_to_cpu(v.i_rdev);
stbuf->st_atime = super.build_time;
stbuf->st_mtime = super.build_time;
stbuf->st_ctime = super.build_time;
diff --git a/fuse/main.c b/fuse/main.c
index fa9795e..8841013 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -113,6 +113,7 @@ static void signal_handle_sigsegv(int signal)
}
static struct fuse_operations erofs_ops = {
+ .readlink = erofs_readlink,
.getattr = erofs_getattr,
.readdir = erofs_readdir,
.open = erofs_open,
diff --git a/fuse/namei.c b/fuse/namei.c
index 3503a8d..7ed1168 100644
--- a/fuse/namei.c
+++ b/fuse/namei.c
@@ -11,6 +11,7 @@
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include "erofs/defs.h"
#include "logging.h"
@@ -39,6 +40,13 @@ static uint8_t get_path_token_len(const char *path)
return len;
}
+static inline dev_t new_decode_dev(u32 dev)
+{
+ unsigned major = (dev & 0xfff00) >> 8;
+ unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+ return makedev(major, minor);
+}
+
int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
{
int ret;
@@ -65,13 +73,11 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
switch (vi->i_mode & S_IFMT) {
case S_IFBLK:
case S_IFCHR:
- /* fixme: add special devices support
- * vi->i_rdev = new_decode_dev(le32_to_cpu(v1->i_u.rdev));
- */
+ vi->i_rdev = new_decode_dev(le32_to_cpu(v1->i_u.rdev));
break;
case S_IFIFO:
case S_IFSOCK:
- /*fixme: vi->i_rdev = 0; */
+ vi->i_rdev = 0;
break;
case S_IFREG:
case S_IFLNK:
diff --git a/fuse/read.c b/fuse/read.c
index ffe976e..3ce5c4f 100644
--- a/fuse/read.c
+++ b/fuse/read.c
@@ -112,3 +112,29 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
return -EINVAL;
}
}
+
+int erofs_readlink(const char *path, char *buffer, size_t size)
+{
+ int ret;
+ erofs_nid_t nid;
+ size_t lnksz;
+ struct erofs_vnode v;
+
+ ret = walk_path(path, &nid);
+ if (ret)
+ return ret;
+
+ ret = erofs_iget_by_nid(nid, &v);
+ if (ret)
+ return ret;
+
+ lnksz = min((size_t)v.i_size, size - 1);
+
+ ret = erofs_read(path, buffer, lnksz, 0, NULL);
+ buffer[lnksz] = '\0';
+ if (ret != (int)lnksz)
+ return ret;
+
+ logi("path:%s link=%s size=%llu", path, buffer, lnksz);
+ return 0;
+}
\ No newline at end of file
diff --git a/fuse/read.h b/fuse/read.h
index f2fcebe..89d4b4c 100644
--- a/fuse/read.h
+++ b/fuse/read.h
@@ -12,5 +12,6 @@
int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
struct fuse_file_info *fi);
+int erofs_readlink(const char *path, char *buffer, size_t size);
#endif
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index cba3ce4..47ad96d 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -153,6 +153,7 @@ struct erofs_vnode {
uint16_t i_uid;
uint16_t i_gid;
uint16_t i_nlink;
+ uint32_t i_rdev;
/* if file is inline read inline data witch inode */
char *idata;
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 5/5] erofs-utils: support read compressed file
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
` (2 preceding siblings ...)
2020-10-15 13:39 ` [PATCH 4/5] erofs-utils: support read special file Huang Jianan
@ 2020-10-15 13:39 ` Huang Jianan
2020-10-16 16:17 ` Gao Xiang via Linux-erofs
2020-10-16 15:41 ` [PATCH 1/5] erofs-utils: fix the conflict with the master branch Gao Xiang via Linux-erofs
4 siblings, 1 reply; 10+ messages in thread
From: Huang Jianan @ 2020-10-15 13:39 UTC (permalink / raw)
To: linux-erofs; +Cc: guoweichao, zhangshiming
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Guo Weichao <guoweichao@oppo.com>
---
fuse/Makefile.am | 8 +-
fuse/decompress.c | 86 ++++++++
fuse/decompress.h | 37 ++++
fuse/dentry.h | 5 +-
fuse/init.c | 22 ++-
fuse/init.h | 2 +
fuse/namei.c | 11 +-
fuse/read.c | 71 ++++++-
fuse/zmap.c | 416 +++++++++++++++++++++++++++++++++++++++
include/erofs/defs.h | 13 ++
include/erofs/internal.h | 43 +++-
include/erofs_fs.h | 4 +
12 files changed, 710 insertions(+), 8 deletions(-)
create mode 100644 fuse/decompress.c
create mode 100644 fuse/decompress.h
create mode 100644 fuse/zmap.c
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
index fffd67a..052c716 100644
--- a/fuse/Makefile.am
+++ b/fuse/Makefile.am
@@ -3,12 +3,16 @@
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = erofsfuse
-erofsfuse_SOURCES = main.c dentry.c getattr.c logging.c namei.c read.c disk_io.c init.c open.c readir.c
+erofsfuse_SOURCES = main.c dentry.c getattr.c logging.c namei.c read.c disk_io.c init.c open.c readir.c zmap.c
+if ENABLE_LZ4
+erofsfuse_SOURCES += decompress.c
+endif
erofsfuse_CFLAGS = -Wall -Werror -Wextra \
+ -Wno-implicit-fallthrough \
-I$(top_srcdir)/include \
$(shell pkg-config fuse --cflags) \
-DFUSE_USE_VERSION=26 \
-std=gnu99
LDFLAGS += $(shell pkg-config fuse --libs)
-erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la -ldl
+erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la -ldl ${LZ4_LIBS}
diff --git a/fuse/decompress.c b/fuse/decompress.c
new file mode 100644
index 0000000..e2df3ce
--- /dev/null
+++ b/fuse/decompress.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
+ * Created by Huang Jianan <huangjianan@oppo.com>
+ */
+
+#include <stdlib.h>
+#include <lz4.h>
+
+#include "erofs/internal.h"
+#include "erofs/err.h"
+#include "decompress.h"
+#include "logging.h"
+#include "init.h"
+
+static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq)
+{
+ char *dest = rq->out + rq->ofs_out;
+ char *src = rq->in + rq->ofs_head;
+
+ memcpy(dest, src, rq->outputsize - rq->ofs_head);
+
+ return 0;
+}
+
+static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq)
+{
+ int ret = 0;
+ char *dest = rq->out + rq->ofs_out;
+ char *src = rq->in;
+ char *buff = NULL;
+ bool support_0padding = false;
+ unsigned int inputmargin = 0;
+
+ if (sbk->feature_incompat & EROFS_FEATURE_INCOMPAT_LZ4_0PADDING) {
+ support_0padding = true;
+
+ while (!src[inputmargin & ~PAGE_MASK])
+ if (!(++inputmargin & ~PAGE_MASK))
+ break;
+
+ if (inputmargin >= rq->inputsize)
+ return -EIO;
+ }
+
+ if (rq->ofs_head) {
+ buff = malloc(rq->outputsize);
+ if (!buff)
+ return -ENOMEM;
+ dest = buff;
+ }
+
+ if (rq->partial_decoding || !support_0padding)
+ ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
+ rq->inputsize - inputmargin,
+ rq->outputsize, rq->outputsize);
+ else
+ ret = LZ4_decompress_safe(src + inputmargin, dest,
+ rq->inputsize - inputmargin,
+ rq->outputsize);
+
+ if (ret != (int)rq->outputsize) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (rq->ofs_head) {
+ src = dest + rq->ofs_head;
+ dest = rq->out + rq->ofs_out;
+ memcpy(dest, src, rq->outputsize - rq->ofs_head);
+ }
+
+out:
+ if (buff)
+ free(buff);
+
+ return ret;
+}
+
+int z_erofs_decompress(struct z_erofs_decompress_req *rq)
+{
+ if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)
+ return z_erofs_shifted_transform(rq);
+
+ return z_erofs_decompress_generic(rq);
+}
diff --git a/fuse/decompress.h b/fuse/decompress.h
new file mode 100644
index 0000000..cd395c3
--- /dev/null
+++ b/fuse/decompress.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * 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 "erofs/internal.h"
+
+enum {
+ Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
+ Z_EROFS_COMPRESSION_RUNTIME_MAX
+};
+
+struct z_erofs_decompress_req {
+ char *in, *out;
+
+ size_t ofs_out, ofs_head;
+ unsigned int inputsize, outputsize;
+
+ /* indicate the algorithm will be used for decompression */
+ unsigned int alg;
+ bool partial_decoding;
+};
+
+#ifdef LZ4_ENABLED
+int z_erofs_decompress(struct z_erofs_decompress_req *rq);
+#else
+int z_erofs_decompress(struct z_erofs_decompress_req *rq)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/fuse/dentry.h b/fuse/dentry.h
index ee2144d..f89c506 100644
--- a/fuse/dentry.h
+++ b/fuse/dentry.h
@@ -10,10 +10,11 @@
#include <stdint.h>
#include "erofs/internal.h"
+/* fixme: Deal with names that exceed the allocated size */
#ifdef __64BITS
-#define DCACHE_ENTRY_NAME_LEN 40
+#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
#else
-#define DCACHE_ENTRY_NAME_LEN 48
+#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
#endif
/* This struct declares a node of a k-tree. Every node has a pointer to one of
diff --git a/fuse/init.c b/fuse/init.c
index 8198fa7..e9cc9f8 100644
--- a/fuse/init.c
+++ b/fuse/init.c
@@ -17,7 +17,23 @@
struct erofs_super_block super;
-static struct erofs_super_block *sbk = &super;
+struct erofs_super_block *sbk = &super;
+
+static bool check_layout_compatibility(struct erofs_super_block *sb,
+ struct erofs_super_block *dsb)
+{
+ const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
+
+ sb->feature_incompat = feature;
+
+ /* check if current kernel meets all mandatory requirements */
+ if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {
+ loge("unidentified incompatible feature %x, please upgrade kernel version",
+ feature & ~EROFS_ALL_FEATURE_INCOMPAT);
+ return false;
+ }
+ return true;
+}
int erofs_init_super(void)
{
@@ -40,6 +56,9 @@ int erofs_init_super(void)
return -EINVAL;
}
+ if (!check_layout_compatibility(sbk, sb))
+ return -EINVAL;
+
sbk->checksum = le32_to_cpu(sb->checksum);
sbk->feature_compat = le32_to_cpu(sb->feature_compat);
sbk->blkszbits = sb->blkszbits;
@@ -56,6 +75,7 @@ int erofs_init_super(void)
sbk->root_nid = le16_to_cpu(sb->root_nid);
logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));
+ logp("%-15s:0x%X", STR(feature_incompat), SUPER_MEM(feature_incompat));
logp("%-15s:0x%X", STR(feature_compat), SUPER_MEM(feature_compat));
logp("%-15s:%u", STR(blkszbits), SUPER_MEM(blkszbits));
logp("%-15s:%u", STR(root_nid), SUPER_MEM(root_nid));
diff --git a/fuse/init.h b/fuse/init.h
index d7a97b5..3fc4eb5 100644
--- a/fuse/init.h
+++ b/fuse/init.h
@@ -13,6 +13,8 @@
#define BOOT_SECTOR_SIZE 0x400
+extern struct erofs_super_block *sbk;
+
int erofs_init_super(void);
erofs_nid_t erofs_get_root_nid(void);
erofs_off_t nid2addr(erofs_nid_t nid);
diff --git a/fuse/namei.c b/fuse/namei.c
index 7ed1168..510fcfd 100644
--- a/fuse/namei.c
+++ b/fuse/namei.c
@@ -49,7 +49,7 @@ static inline dev_t new_decode_dev(u32 dev)
int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
{
- int ret;
+ int ret, ifmt;
char buf[EROFS_BLKSIZ];
struct erofs_inode_compact *v1;
const erofs_off_t addr = nid2addr(nid);
@@ -60,6 +60,11 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
return -EIO;
v1 = (struct erofs_inode_compact *)buf;
+ /* fixme: support extended inode */
+ ifmt = le16_to_cpu(v1->i_format);
+ if (__inode_version(ifmt) != EROFS_INODE_LAYOUT_COMPACT)
+ return -EOPNOTSUPP;
+
vi->datalayout = __inode_data_mapping(le16_to_cpu(v1->i_format));
vi->inode_isize = sizeof(struct erofs_inode_compact);
vi->xattr_isize = erofs_xattr_ibody_size(v1->i_xattr_icount);
@@ -88,6 +93,10 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
return -EIO;
}
+ vi->z_inited = false;
+ if (erofs_inode_is_data_compressed(vi->datalayout))
+ z_erofs_fill_inode(vi);
+
return 0;
}
diff --git a/fuse/read.c b/fuse/read.c
index 3ce5c4f..cc0781f 100644
--- a/fuse/read.c
+++ b/fuse/read.c
@@ -16,6 +16,7 @@
#include "namei.h"
#include "disk_io.h"
#include "init.h"
+#include "decompress.h"
size_t erofs_read_data(struct erofs_vnode *vnode, char *buffer,
size_t size, off_t offset)
@@ -78,6 +79,73 @@ finished:
}
+size_t erofs_read_data_compression(struct erofs_vnode *vnode, char *buffer,
+ size_t size, off_t offset)
+{
+ int ret;
+ size_t end, count, ofs, sum = size;
+ struct erofs_map_blocks map = {
+ .index = UINT_MAX,
+ };
+ bool partial;
+ unsigned int algorithmformat;
+ char raw[EROFS_BLKSIZ];
+
+ while (sum) {
+ end = offset + sum;
+ map.m_la = end - 1;
+
+ ret = z_erofs_map_blocks_iter(vnode, &map);
+ if (ret)
+ return ret;
+
+ if (!(map.m_flags & EROFS_MAP_MAPPED)) {
+ sum -= map.m_llen;
+ continue;
+ }
+
+ ret = dev_read(raw, EROFS_BLKSIZ, map.m_pa);
+ if (ret < 0 || (size_t)ret != EROFS_BLKSIZ)
+ return -EIO;
+
+ algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?
+ Z_EROFS_COMPRESSION_LZ4 :
+ Z_EROFS_COMPRESSION_SHIFTED;
+
+ if (end >= map.m_la + map.m_llen) {
+ count = map.m_llen;
+ partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
+ } else {
+ count = end - map.m_la;
+ partial = true;
+ }
+
+ if ((off_t)map.m_la < offset) {
+ ofs = offset - map.m_la;
+ sum = 0;
+ } else {
+ ofs = 0;
+ sum -= count;
+ }
+
+ ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
+ .in = raw,
+ .out = buffer,
+ .ofs_out = sum,
+ .ofs_head = ofs,
+ .inputsize = EROFS_BLKSIZ,
+ .outputsize = count,
+ .alg = algorithmformat,
+ .partial_decoding = partial
+ });
+ if (ret < 0)
+ return ret;
+ }
+
+ logi("nid:%u size=%zd offset=%llu done",
+ vnode->nid, size, (long long)offset);
+ return size;
+}
int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
struct fuse_file_info *fi)
@@ -107,7 +175,8 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
case EROFS_INODE_FLAT_COMPRESSION:
- /* Fixme: */
+ return erofs_read_data_compression(&v, buffer, size, offset);
+
default:
return -EINVAL;
}
diff --git a/fuse/zmap.c b/fuse/zmap.c
new file mode 100644
index 0000000..022ca1b
--- /dev/null
+++ b/fuse/zmap.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Many parts of codes are copied from Linux kernel/fs/erofs.
+ *
+ * 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 "init.h"
+#include "disk_io.h"
+#include "logging.h"
+
+int z_erofs_fill_inode(struct erofs_vnode *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->z_inited = true;
+ }
+
+ return 0;
+}
+
+static int z_erofs_fill_inode_lazy(struct erofs_vnode *vi)
+{
+ int ret;
+ erofs_off_t pos;
+ struct z_erofs_map_header *h;
+ char buf[8];
+
+ if (vi->z_inited)
+ return 0;
+
+ DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
+
+ pos = round_up(nid2addr(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
+
+ ret = dev_read(buf, 8, pos);
+ if (ret < 0 && (uint32_t)ret != 8)
+ 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) {
+ loge("unknown compression format %u for nid %llu",
+ vi->z_algorithmtype[0], 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) {
+ loge("unsupported physical clusterbits %u for nid %llu",
+ vi->z_physical_clusterbits[0], vi->nid);
+ return -EOPNOTSUPP;
+ }
+
+ vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits +
+ ((h->h_clusterbits >> 5) & 7);
+ vi->z_inited = true;
+
+ return 0;
+}
+
+struct z_erofs_maprecorder {
+ struct erofs_vnode *vnode;
+ 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 = dev_read(mpage, EROFS_BLKSIZ, blknr_to_addr(eblk));
+ if (ret < 0 && (uint32_t)ret != EROFS_BLKSIZ)
+ return -EIO;
+
+ map->index = eblk;
+
+ return 0;
+}
+
+static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,
+ unsigned long lcn)
+{
+ struct erofs_vnode *const vi = m->vnode;
+ const erofs_off_t ibase = nid2addr(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_vnode *const vi = m->vnode;
+ 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_vnode *const vi = m->vnode;
+ const unsigned int lclusterbits = vi->z_logical_clusterbits;
+ const erofs_off_t ebase = round_up(nid2addr(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->vnode->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_vnode *const vi = m->vnode;
+ 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) {
+ loge("bogus lookback distance @ nid %llu", 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]) {
+ loge("invalid lookback distance 0 @ nid %llu",
+ 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:
+ loge("unknown type %u @ lcn %lu of nid %llu",
+ m->type, lcn, vi->nid);
+ DBG_BUGON(1);
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+int z_erofs_map_blocks_iter(struct erofs_vnode *vi,
+ struct erofs_map_blocks *map)
+{
+ struct z_erofs_maprecorder m = {
+ .vnode = 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) {
+ loge("invalid logical cluster 0 at nid %llu",
+ 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:
+ loge("unknown type %u @ offset %llu of nid %llu",
+ m.type, ofs, 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:
+ logd("m_la %llu m_pa %llu m_llen %llu m_plen %llu 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/include/erofs/defs.h b/include/erofs/defs.h
index a9c769e..06d29a9 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -173,5 +173,18 @@ typedef int64_t s64;
#define __maybe_unused __attribute__((__unused__))
#endif
+struct __una_u32 { u32 x; } __packed;
+
+static inline u32 __get_unaligned_cpu32(const void *p)
+{
+ const struct __una_u32 *ptr = (const struct __una_u32 *)p;
+ return ptr->x;
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+ return __get_unaligned_cpu32((const u8 *)p);
+}
+
#endif
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 47ad96d..5807b67 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -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)
@@ -145,7 +147,15 @@ struct erofs_vnode {
uint16_t xattr_shared_count;
char *xattr_shared_xattrs;
- erofs_blk_t raw_blkaddr;
+ union {
+ erofs_blk_t raw_blkaddr;
+ struct {
+ uint16_t z_advise;
+ uint8_t z_algorithmtype[2];
+ uint8_t z_logical_clusterbits;
+ uint8_t z_physical_clusterbits[2];
+ };
+ };
erofs_nid_t nid;
uint32_t i_ino;
@@ -155,6 +165,7 @@ struct erofs_vnode {
uint16_t i_nlink;
uint32_t i_rdev;
+ bool z_inited;
/* if file is inline read inline data witch inode */
char *idata;
};
@@ -207,5 +218,35 @@ static inline const char *erofs_strerror(int err)
return msg;
}
+enum {
+ BH_Mapped ,
+ BH_Zipped ,
+ BH_FullMapped,
+};
+
+/* Has a disk mapping */
+#define EROFS_MAP_MAPPED (1 << BH_Mapped)
+/* 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 {
+ erofs_off_t m_pa, m_la;
+ u64 m_plen, m_llen;
+
+ unsigned int m_flags;
+
+ char mpage[EROFS_BLKSIZ];
+ erofs_blk_t index;
+};
+
+/* zmap.c */
+int z_erofs_fill_inode(struct erofs_vnode *vi);
+int z_erofs_map_blocks_iter(struct erofs_vnode *vi,
+ struct erofs_map_blocks *map);
+
+#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
+
#endif
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index bcc4f0c..0c70897 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -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))
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content.
2020-10-15 13:39 ` [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content Huang Jianan
@ 2020-10-16 15:36 ` Gao Xiang via Linux-erofs
0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang via Linux-erofs @ 2020-10-16 15:36 UTC (permalink / raw)
To: Huang Jianan; +Cc: guoweichao, linux-erofs, zhangshiming
On Thu, Oct 15, 2020 at 09:39:57PM +0800, Huang Jianan wrote:
> Signed-off-by: Huang Jianan <huangjianan@oppo.com>
> Signed-off-by: Guo Weichao <guoweichao@oppo.com>
> ---
> fuse/namei.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fuse/namei.c b/fuse/namei.c
> index 21e6ba0..3503a8d 100644
> --- a/fuse/namei.c
> +++ b/fuse/namei.c
> @@ -158,7 +158,7 @@ struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
> if (v.datalayout == EROFS_INODE_FLAT_INLINE) {
> uint32_t dir_off = erofs_blkoff(dirsize);
> off_t dir_addr = nid2addr(dcache_get_nid(parent))
> - + sizeof(struct erofs_inode_compact);
> + + sizeof(struct erofs_inode_compact) + v.xattr_isize;
hmm... just considering this line, I think it should be
+ off_t dir_addr = nid2addr(dcache_get_nid(parent)) +
+ v.inode_isize + v.xattr_isize;
btw, I've fixed it, will send out the next integrated WIP
erofsfuse patchset.
Thanks,
Gao Xiang
>
> memset(buf, 0, sizeof(buf));
> ret = dev_read(buf, dir_off, dir_addr);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/5] erofs-utils: fix the conflict with the master branch
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
` (3 preceding siblings ...)
2020-10-15 13:39 ` [PATCH 5/5] erofs-utils: support read compressed file Huang Jianan
@ 2020-10-16 15:41 ` Gao Xiang via Linux-erofs
4 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang via Linux-erofs @ 2020-10-16 15:41 UTC (permalink / raw)
To: Huang Jianan; +Cc: guoweichao, linux-erofs, zhangshiming
On Thu, Oct 15, 2020 at 09:39:55PM +0800, Huang Jianan wrote:
> The current fuse branch is quite different from the master branch.
> So fix the conflict with the master branch to support the upcoming patch.
>
> Signed-off-by: Huang Jianan <huangjianan@oppo.com>
> Signed-off-by: Guo Weichao <guoweichao@oppo.com>
Looks good (although the original erofsfuse codebase
really needs to be cleaned up),
Reviewed-by: Gao Xiang <hsiangkao@aol.com>
( btw, I'd like to fold this one together with the
original erofsfuse patch and resend. )
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 5/5] erofs-utils: support read compressed file
2020-10-15 13:39 ` [PATCH 5/5] erofs-utils: support read compressed file Huang Jianan
@ 2020-10-16 16:17 ` Gao Xiang via Linux-erofs
2020-10-17 6:24 ` jnhuang95
0 siblings, 1 reply; 10+ messages in thread
From: Gao Xiang via Linux-erofs @ 2020-10-16 16:17 UTC (permalink / raw)
To: Huang Jianan; +Cc: guoweichao, linux-erofs, zhangshiming
Hi Jianan,
On Thu, Oct 15, 2020 at 09:39:59PM +0800, Huang Jianan wrote:
> Signed-off-by: Huang Jianan <huangjianan@oppo.com>
> Signed-off-by: Guo Weichao <guoweichao@oppo.com>
I read the logic below, and generally it looks good! <thumb>
I'm now handling some minor thing about this and I will
update as the following patch to speed up the development.
> ---
...
> diff --git a/fuse/decompress.c b/fuse/decompress.c
> new file mode 100644
> index 0000000..e2df3ce
> --- /dev/null
> +++ b/fuse/decompress.c
we might need to move this file to lib/decompress.c
> @@ -0,0 +1,86 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
> + * Created by Huang Jianan <huangjianan@oppo.com>
> + */
> +
> +#include <stdlib.h>
> +#include <lz4.h>
> +
> +#include "erofs/internal.h"
> +#include "erofs/err.h"
> +#include "decompress.h"
> +#include "logging.h"
> +#include "init.h"
> +
> +static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq)
> +{
> + char *dest = rq->out + rq->ofs_out;
I think ofs_out can be omited, since we only care about the dest buffer here.
so (new) rq->out == (old)rq->out + rq->ofs_out.
> + char *src = rq->in + rq->ofs_head;
need to judge if the outputsize is larger than EROFS_BLKSIZ, since it's
undefined on-disk behavior, and can be implemented in the future
on-disk update.
> +
> + memcpy(dest, src, rq->outputsize - rq->ofs_head);
> +
> + return 0;
> +}
And this function can be folded into z_erofs_decompress(), because the
implementation here is much simple enough.
> +
> +static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq)
it might be renamed into z_erofs_decompress_lz4.
> +{
> + int ret = 0;
> + char *dest = rq->out + rq->ofs_out;
> + char *src = rq->in;
> + char *buff = NULL;
> + bool support_0padding = false;
> + unsigned int inputmargin = 0;
> +
> + if (sbk->feature_incompat & EROFS_FEATURE_INCOMPAT_LZ4_0PADDING) {
> + support_0padding = true;
> +
> + while (!src[inputmargin & ~PAGE_MASK])
> + if (!(++inputmargin & ~PAGE_MASK))
> + break;
> +
> + if (inputmargin >= rq->inputsize)
> + return -EIO;
> + }
> +
> + if (rq->ofs_head) {
we might need to rethink about the name of ofs_head.
> + buff = malloc(rq->outputsize);
> + if (!buff)
> + return -ENOMEM;
> + dest = buff;
need some cleanup, and dest variable is assigned for several times,
but without making the logic simpler.
> + }
> +
> + if (rq->partial_decoding || !support_0padding)
> + ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
> + rq->inputsize - inputmargin,
> + rq->outputsize, rq->outputsize);
> + else
> + ret = LZ4_decompress_safe(src + inputmargin, dest,
> + rq->inputsize - inputmargin,
> + rq->outputsize);
> +
> + if (ret != (int)rq->outputsize) {
> + ret = -EIO;
> + goto out;
> + }
> +
> + if (rq->ofs_head) {
> + src = dest + rq->ofs_head;
> + dest = rq->out + rq->ofs_out;
> + memcpy(dest, src, rq->outputsize - rq->ofs_head);
> + }
> +
> +out:
> + if (buff)
> + free(buff);
> +
> + return ret;
> +}
> +
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)
> +{
> + if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)
> + return z_erofs_shifted_transform(rq);
> +
> + return z_erofs_decompress_generic(rq);
> +}
> diff --git a/fuse/decompress.h b/fuse/decompress.h
> new file mode 100644
> index 0000000..cd395c3
> --- /dev/null
> +++ b/fuse/decompress.h
move this file to include/erofs/decompress.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * 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 "erofs/internal.h"
> +
> +enum {
> + Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
> + Z_EROFS_COMPRESSION_RUNTIME_MAX
> +};
> +
> +struct z_erofs_decompress_req {
> + char *in, *out;
> +
> + size_t ofs_out, ofs_head;
> + unsigned int inputsize, outputsize;
> +
> + /* indicate the algorithm will be used for decompression */
> + unsigned int alg;
> + bool partial_decoding;
> +};
> +
> +#ifdef LZ4_ENABLED
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq);
> +#else
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)
> +{
> + return -EOPNOTSUPP;
> +}
> +#endif
> +
> +#endif
> diff --git a/fuse/dentry.h b/fuse/dentry.h
> index ee2144d..f89c506 100644
> --- a/fuse/dentry.h
> +++ b/fuse/dentry.h
> @@ -10,10 +10,11 @@
> #include <stdint.h>
> #include "erofs/internal.h"
>
> +/* fixme: Deal with names that exceed the allocated size */
> #ifdef __64BITS
> -#define DCACHE_ENTRY_NAME_LEN 40
> +#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
> #else
> -#define DCACHE_ENTRY_NAME_LEN 48
> +#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
> #endif
not related to this patch, does it relate to another preexist
bug about erofsfuse codebase?
>
> /* This struct declares a node of a k-tree. Every node has a pointer to one of
> diff --git a/fuse/init.c b/fuse/init.c
> index 8198fa7..e9cc9f8 100644
> --- a/fuse/init.c
> +++ b/fuse/init.c
> @@ -17,7 +17,23 @@
>
>
> struct erofs_super_block super;
> -static struct erofs_super_block *sbk = &super;
> +struct erofs_super_block *sbk = &super;
> +
> +static bool check_layout_compatibility(struct erofs_super_block *sb,
> + struct erofs_super_block *dsb)
> +{
> + const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
> +
> + sb->feature_incompat = feature;
> +
> + /* check if current kernel meets all mandatory requirements */
> + if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {
> + loge("unidentified incompatible feature %x, please upgrade kernel version",
> + feature & ~EROFS_ALL_FEATURE_INCOMPAT);
> + return false;
> + }
> + return true;
> +}
>
> int erofs_init_super(void)
> {
> @@ -40,6 +56,9 @@ int erofs_init_super(void)
> return -EINVAL;
> }
>
> + if (!check_layout_compatibility(sbk, sb))
> + return -EINVAL;
> +
> sbk->checksum = le32_to_cpu(sb->checksum);
> sbk->feature_compat = le32_to_cpu(sb->feature_compat);
> sbk->blkszbits = sb->blkszbits;
> @@ -56,6 +75,7 @@ int erofs_init_super(void)
> sbk->root_nid = le16_to_cpu(sb->root_nid);
>
> logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));
> + logp("%-15s:0x%X", STR(feature_incompat), SUPER_MEM(feature_incompat));
> logp("%-15s:0x%X", STR(feature_compat), SUPER_MEM(feature_compat));
> logp("%-15s:%u", STR(blkszbits), SUPER_MEM(blkszbits));
> logp("%-15s:%u", STR(root_nid), SUPER_MEM(root_nid));
> diff --git a/fuse/init.h b/fuse/init.h
> index d7a97b5..3fc4eb5 100644
> --- a/fuse/init.h
> +++ b/fuse/init.h
> @@ -13,6 +13,8 @@
>
> #define BOOT_SECTOR_SIZE 0x400
>
> +extern struct erofs_super_block *sbk;
> +
> int erofs_init_super(void);
> erofs_nid_t erofs_get_root_nid(void);
> erofs_off_t nid2addr(erofs_nid_t nid);
> diff --git a/fuse/namei.c b/fuse/namei.c
> index 7ed1168..510fcfd 100644
> --- a/fuse/namei.c
> +++ b/fuse/namei.c
> @@ -49,7 +49,7 @@ static inline dev_t new_decode_dev(u32 dev)
>
> int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> {
> - int ret;
> + int ret, ifmt;
> char buf[EROFS_BLKSIZ];
> struct erofs_inode_compact *v1;
> const erofs_off_t addr = nid2addr(nid);
> @@ -60,6 +60,11 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> return -EIO;
>
> v1 = (struct erofs_inode_compact *)buf;
> + /* fixme: support extended inode */
> + ifmt = le16_to_cpu(v1->i_format);
> + if (__inode_version(ifmt) != EROFS_INODE_LAYOUT_COMPACT)
> + return -EOPNOTSUPP;
> +
> vi->datalayout = __inode_data_mapping(le16_to_cpu(v1->i_format));
> vi->inode_isize = sizeof(struct erofs_inode_compact);
> vi->xattr_isize = erofs_xattr_ibody_size(v1->i_xattr_icount);
> @@ -88,6 +93,10 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> return -EIO;
> }
>
> + vi->z_inited = false;
> + if (erofs_inode_is_data_compressed(vi->datalayout))
> + z_erofs_fill_inode(vi);
> +
> return 0;
> }
>
> diff --git a/fuse/read.c b/fuse/read.c
> index 3ce5c4f..cc0781f 100644
> --- a/fuse/read.c
> +++ b/fuse/read.c
> @@ -16,6 +16,7 @@
> #include "namei.h"
> #include "disk_io.h"
> #include "init.h"
> +#include "decompress.h"
>
> size_t erofs_read_data(struct erofs_vnode *vnode, char *buffer,
> size_t size, off_t offset)
> @@ -78,6 +79,73 @@ finished:
>
> }
>
> +size_t erofs_read_data_compression(struct erofs_vnode *vnode, char *buffer,
> + size_t size, off_t offset)
> +{
> + int ret;
> + size_t end, count, ofs, sum = size;
> + struct erofs_map_blocks map = {
> + .index = UINT_MAX,
> + };
> + bool partial;
> + unsigned int algorithmformat;
> + char raw[EROFS_BLKSIZ];
> +
> + while (sum) {
> + end = offset + sum;
> + map.m_la = end - 1;
> +
> + ret = z_erofs_map_blocks_iter(vnode, &map);
> + if (ret)
> + return ret;
> +
> + if (!(map.m_flags & EROFS_MAP_MAPPED)) {
> + sum -= map.m_llen;
> + continue;
> + }
> +
> + ret = dev_read(raw, EROFS_BLKSIZ, map.m_pa);
> + if (ret < 0 || (size_t)ret != EROFS_BLKSIZ)
> + return -EIO;
> +
> + algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?
> + Z_EROFS_COMPRESSION_LZ4 :
> + Z_EROFS_COMPRESSION_SHIFTED;
> +
> + if (end >= map.m_la + map.m_llen) {
> + count = map.m_llen;
> + partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
> + } else {
> + count = end - map.m_la;
> + partial = true;
> + }
it would be better to make the logic more explicitly, like:
- if (end >= map.m_la + map.m_llen) {
- count = map.m_llen;
- partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
- } else {
+ /*
+ * 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) {
count = end - map.m_la;
partial = true;
+ } else {
+ ASSERT(end == map.m_la + map_m_llen);
+ count = map.m_llen;
+ partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
}
> +
> + if ((off_t)map.m_la < offset) {
> + ofs = offset - map.m_la;
> + sum = 0;
> + } else {
> + ofs = 0;
> + sum -= count;
> + }
a bit weird here, anyway, let me clean up here as well.
> +
> + ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
> + .in = raw,
> + .out = buffer,
> + .ofs_out = sum,
> + .ofs_head = ofs,
> + .inputsize = EROFS_BLKSIZ,
> + .outputsize = count,
> + .alg = algorithmformat,
> + .partial_decoding = partial
> + });
> + if (ret < 0)
> + return ret;
> + }
> +
> + logi("nid:%u size=%zd offset=%llu done",
> + vnode->nid, size, (long long)offset);
> + return size;
> +}
>
> int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
> struct fuse_file_info *fi)
> @@ -107,7 +175,8 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
>
> case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> case EROFS_INODE_FLAT_COMPRESSION:
> - /* Fixme: */
> + return erofs_read_data_compression(&v, buffer, size, offset);
> +
> default:
> return -EINVAL;
> }
> diff --git a/fuse/zmap.c b/fuse/zmap.c
> new file mode 100644
> index 0000000..022ca1b
> --- /dev/null
> +++ b/fuse/zmap.c
let's move this file to lib/ as well.
> @@ -0,0 +1,416 @@
> +// SPDX-License-Identifier: GPL-2.0-only
Let's relicense this file to GPL-2.0+ for erofs-utils
(since I'm the original author, and assume you agree on this as well..)
> +/*
> + * Many parts of codes are copied from Linux kernel/fs/erofs.
> + *
> + * 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 "init.h"
> +#include "disk_io.h"
> +#include "logging.h"
> +
> +int z_erofs_fill_inode(struct erofs_vnode *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->z_inited = true;
> + }
> +
> + return 0;
> +}
> +
> +static int z_erofs_fill_inode_lazy(struct erofs_vnode *vi)
> +{
> + int ret;
> + erofs_off_t pos;
> + struct z_erofs_map_header *h;
> + char buf[8];
> +
> + if (vi->z_inited)
> + return 0;
> +
> + DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
> +
> + pos = round_up(nid2addr(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
> +
> + ret = dev_read(buf, 8, pos);
> + if (ret < 0 && (uint32_t)ret != 8)
> + 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) {
> + loge("unknown compression format %u for nid %llu",
> + vi->z_algorithmtype[0], 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) {
> + loge("unsupported physical clusterbits %u for nid %llu",
> + vi->z_physical_clusterbits[0], vi->nid);
> + return -EOPNOTSUPP;
> + }
> +
> + vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits +
> + ((h->h_clusterbits >> 5) & 7);
> + vi->z_inited = true;
> +
> + return 0;
> +}
> +
> +struct z_erofs_maprecorder {
> + struct erofs_vnode *vnode;
> + 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 = dev_read(mpage, EROFS_BLKSIZ, blknr_to_addr(eblk));
> + if (ret < 0 && (uint32_t)ret != EROFS_BLKSIZ)
> + return -EIO;
> +
> + map->index = eblk;
> +
> + return 0;
> +}
> +
> +static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,
> + unsigned long lcn)
> +{
> + struct erofs_vnode *const vi = m->vnode;
> + const erofs_off_t ibase = nid2addr(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_vnode *const vi = m->vnode;
> + 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_vnode *const vi = m->vnode;
> + const unsigned int lclusterbits = vi->z_logical_clusterbits;
> + const erofs_off_t ebase = round_up(nid2addr(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->vnode->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_vnode *const vi = m->vnode;
> + 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) {
> + loge("bogus lookback distance @ nid %llu", 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]) {
> + loge("invalid lookback distance 0 @ nid %llu",
> + 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:
> + loge("unknown type %u @ lcn %lu of nid %llu",
> + m->type, lcn, vi->nid);
> + DBG_BUGON(1);
> + return -EOPNOTSUPP;
> + }
> + return 0;
> +}
> +
> +int z_erofs_map_blocks_iter(struct erofs_vnode *vi,
> + struct erofs_map_blocks *map)
> +{
> + struct z_erofs_maprecorder m = {
> + .vnode = 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) {
> + loge("invalid logical cluster 0 at nid %llu",
> + 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:
> + loge("unknown type %u @ offset %llu of nid %llu",
> + m.type, ofs, 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:
> + logd("m_la %llu m_pa %llu m_llen %llu m_plen %llu 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/include/erofs/defs.h b/include/erofs/defs.h
> index a9c769e..06d29a9 100644
> --- a/include/erofs/defs.h
> +++ b/include/erofs/defs.h
> @@ -173,5 +173,18 @@ typedef int64_t s64;
> #define __maybe_unused __attribute__((__unused__))
> #endif
>
> +struct __una_u32 { u32 x; } __packed;
> +
> +static inline u32 __get_unaligned_cpu32(const void *p)
> +{
> + const struct __una_u32 *ptr = (const struct __una_u32 *)p;
> + return ptr->x;
> +}
> +
> +static inline u32 get_unaligned_le32(const void *p)
> +{
> + return __get_unaligned_cpu32((const u8 *)p);
> +}
need to handle big-endian, anyway, minor for now.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Re: [PATCH 5/5] erofs-utils: support read compressed file
2020-10-16 16:17 ` Gao Xiang via Linux-erofs
@ 2020-10-17 6:24 ` jnhuang95
2020-10-17 6:36 ` Gao Xiang via Linux-erofs
0 siblings, 1 reply; 10+ messages in thread
From: jnhuang95 @ 2020-10-17 6:24 UTC (permalink / raw)
To: Gao Xiang; +Cc: zhangshiming, linux-erofs, guoweichao
[-- Attachment #1: Type: text/plain, Size: 25299 bytes --]
Hi Gaoxiang,
Thanks for your review and suggestions. It's great to communicate with outstanding community developers. I have learned a lot and look forward to contribute more patches in the future.
Regards,
Jianan
From: Gao Xiang
Date: 2020-10-17 00:17
To: Huang Jianan
CC: linux-erofs; guoweichao; zhangshiming; huangjianan
Subject: Re: [PATCH 5/5] erofs-utils: support read compressed file
Hi Jianan,
On Thu, Oct 15, 2020 at 09:39:59PM +0800, Huang Jianan wrote:
> Signed-off-by: Huang Jianan <huangjianan@oppo.com>
> Signed-off-by: Guo Weichao <guoweichao@oppo.com>
I read the logic below, and generally it looks good! <thumb>
I'm now handling some minor thing about this and I will
update as the following patch to speed up the development.
> ---
...
> diff --git a/fuse/decompress.c b/fuse/decompress.c
> new file mode 100644
> index 0000000..e2df3ce
> --- /dev/null
> +++ b/fuse/decompress.c
we might need to move this file to lib/decompress.c
> @@ -0,0 +1,86 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.
> + * Created by Huang Jianan <huangjianan@oppo.com>
> + */
> +
> +#include <stdlib.h>
> +#include <lz4.h>
> +
> +#include "erofs/internal.h"
> +#include "erofs/err.h"
> +#include "decompress.h"
> +#include "logging.h"
> +#include "init.h"
> +
> +static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq)
> +{
> + char *dest = rq->out + rq->ofs_out;
I think ofs_out can be omited, since we only care about the dest buffer here.
so (new) rq->out == (old)rq->out + rq->ofs_out.
> + char *src = rq->in + rq->ofs_head;
need to judge if the outputsize is larger than EROFS_BLKSIZ, since it's
undefined on-disk behavior, and can be implemented in the future
on-disk update.
> +
> + memcpy(dest, src, rq->outputsize - rq->ofs_head);
> +
> + return 0;
> +}
And this function can be folded into z_erofs_decompress(), because the
implementation here is much simple enough.
> +
> +static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq)
it might be renamed into z_erofs_decompress_lz4.
> +{
> + int ret = 0;
> + char *dest = rq->out + rq->ofs_out;
> + char *src = rq->in;
> + char *buff = NULL;
> + bool support_0padding = false;
> + unsigned int inputmargin = 0;
> +
> + if (sbk->feature_incompat & EROFS_FEATURE_INCOMPAT_LZ4_0PADDING) {
> + support_0padding = true;
> +
> + while (!src[inputmargin & ~PAGE_MASK])
> + if (!(++inputmargin & ~PAGE_MASK))
> + break;
> +
> + if (inputmargin >= rq->inputsize)
> + return -EIO;
> + }
> +
> + if (rq->ofs_head) {
we might need to rethink about the name of ofs_head.
> + buff = malloc(rq->outputsize);
> + if (!buff)
> + return -ENOMEM;
> + dest = buff;
need some cleanup, and dest variable is assigned for several times,
but without making the logic simpler.
> + }
> +
> + if (rq->partial_decoding || !support_0padding)
> + ret = LZ4_decompress_safe_partial(src + inputmargin, dest,
> + rq->inputsize - inputmargin,
> + rq->outputsize, rq->outputsize);
> + else
> + ret = LZ4_decompress_safe(src + inputmargin, dest,
> + rq->inputsize - inputmargin,
> + rq->outputsize);
> +
> + if (ret != (int)rq->outputsize) {
> + ret = -EIO;
> + goto out;
> + }
> +
> + if (rq->ofs_head) {
> + src = dest + rq->ofs_head;
> + dest = rq->out + rq->ofs_out;
> + memcpy(dest, src, rq->outputsize - rq->ofs_head);
> + }
> +
> +out:
> + if (buff)
> + free(buff);
> +
> + return ret;
> +}
> +
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)
> +{
> + if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)
> + return z_erofs_shifted_transform(rq);
> +
> + return z_erofs_decompress_generic(rq);
> +}
> diff --git a/fuse/decompress.h b/fuse/decompress.h
> new file mode 100644
> index 0000000..cd395c3
> --- /dev/null
> +++ b/fuse/decompress.h
move this file to include/erofs/decompress.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * 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 "erofs/internal.h"
> +
> +enum {
> + Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
> + Z_EROFS_COMPRESSION_RUNTIME_MAX
> +};
> +
> +struct z_erofs_decompress_req {
> + char *in, *out;
> +
> + size_t ofs_out, ofs_head;
> + unsigned int inputsize, outputsize;
> +
> + /* indicate the algorithm will be used for decompression */
> + unsigned int alg;
> + bool partial_decoding;
> +};
> +
> +#ifdef LZ4_ENABLED
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq);
> +#else
> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)
> +{
> + return -EOPNOTSUPP;
> +}
> +#endif
> +
> +#endif
> diff --git a/fuse/dentry.h b/fuse/dentry.h
> index ee2144d..f89c506 100644
> --- a/fuse/dentry.h
> +++ b/fuse/dentry.h
> @@ -10,10 +10,11 @@
> #include <stdint.h>
> #include "erofs/internal.h"
>
> +/* fixme: Deal with names that exceed the allocated size */
> #ifdef __64BITS
> -#define DCACHE_ENTRY_NAME_LEN 40
> +#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
> #else
> -#define DCACHE_ENTRY_NAME_LEN 48
> +#define DCACHE_ENTRY_NAME_LEN EROFS_NAME_LEN
> #endif
not related to this patch, does it relate to another preexist
bug about erofsfuse codebase?
>
> /* This struct declares a node of a k-tree. Every node has a pointer to one of
> diff --git a/fuse/init.c b/fuse/init.c
> index 8198fa7..e9cc9f8 100644
> --- a/fuse/init.c
> +++ b/fuse/init.c
> @@ -17,7 +17,23 @@
>
>
> struct erofs_super_block super;
> -static struct erofs_super_block *sbk = &super;
> +struct erofs_super_block *sbk = &super;
> +
> +static bool check_layout_compatibility(struct erofs_super_block *sb,
> + struct erofs_super_block *dsb)
> +{
> + const unsigned int feature = le32_to_cpu(dsb->feature_incompat);
> +
> + sb->feature_incompat = feature;
> +
> + /* check if current kernel meets all mandatory requirements */
> + if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {
> + loge("unidentified incompatible feature %x, please upgrade kernel version",
> + feature & ~EROFS_ALL_FEATURE_INCOMPAT);
> + return false;
> + }
> + return true;
> +}
>
> int erofs_init_super(void)
> {
> @@ -40,6 +56,9 @@ int erofs_init_super(void)
> return -EINVAL;
> }
>
> + if (!check_layout_compatibility(sbk, sb))
> + return -EINVAL;
> +
> sbk->checksum = le32_to_cpu(sb->checksum);
> sbk->feature_compat = le32_to_cpu(sb->feature_compat);
> sbk->blkszbits = sb->blkszbits;
> @@ -56,6 +75,7 @@ int erofs_init_super(void)
> sbk->root_nid = le16_to_cpu(sb->root_nid);
>
> logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));
> + logp("%-15s:0x%X", STR(feature_incompat), SUPER_MEM(feature_incompat));
> logp("%-15s:0x%X", STR(feature_compat), SUPER_MEM(feature_compat));
> logp("%-15s:%u", STR(blkszbits), SUPER_MEM(blkszbits));
> logp("%-15s:%u", STR(root_nid), SUPER_MEM(root_nid));
> diff --git a/fuse/init.h b/fuse/init.h
> index d7a97b5..3fc4eb5 100644
> --- a/fuse/init.h
> +++ b/fuse/init.h
> @@ -13,6 +13,8 @@
>
> #define BOOT_SECTOR_SIZE 0x400
>
> +extern struct erofs_super_block *sbk;
> +
> int erofs_init_super(void);
> erofs_nid_t erofs_get_root_nid(void);
> erofs_off_t nid2addr(erofs_nid_t nid);
> diff --git a/fuse/namei.c b/fuse/namei.c
> index 7ed1168..510fcfd 100644
> --- a/fuse/namei.c
> +++ b/fuse/namei.c
> @@ -49,7 +49,7 @@ static inline dev_t new_decode_dev(u32 dev)
>
> int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> {
> - int ret;
> + int ret, ifmt;
> char buf[EROFS_BLKSIZ];
> struct erofs_inode_compact *v1;
> const erofs_off_t addr = nid2addr(nid);
> @@ -60,6 +60,11 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> return -EIO;
>
> v1 = (struct erofs_inode_compact *)buf;
> + /* fixme: support extended inode */
> + ifmt = le16_to_cpu(v1->i_format);
> + if (__inode_version(ifmt) != EROFS_INODE_LAYOUT_COMPACT)
> + return -EOPNOTSUPP;
> +
> vi->datalayout = __inode_data_mapping(le16_to_cpu(v1->i_format));
> vi->inode_isize = sizeof(struct erofs_inode_compact);
> vi->xattr_isize = erofs_xattr_ibody_size(v1->i_xattr_icount);
> @@ -88,6 +93,10 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
> return -EIO;
> }
>
> + vi->z_inited = false;
> + if (erofs_inode_is_data_compressed(vi->datalayout))
> + z_erofs_fill_inode(vi);
> +
> return 0;
> }
>
> diff --git a/fuse/read.c b/fuse/read.c
> index 3ce5c4f..cc0781f 100644
> --- a/fuse/read.c
> +++ b/fuse/read.c
> @@ -16,6 +16,7 @@
> #include "namei.h"
> #include "disk_io.h"
> #include "init.h"
> +#include "decompress.h"
>
> size_t erofs_read_data(struct erofs_vnode *vnode, char *buffer,
> size_t size, off_t offset)
> @@ -78,6 +79,73 @@ finished:
>
> }
>
> +size_t erofs_read_data_compression(struct erofs_vnode *vnode, char *buffer,
> + size_t size, off_t offset)
> +{
> + int ret;
> + size_t end, count, ofs, sum = size;
> + struct erofs_map_blocks map = {
> + .index = UINT_MAX,
> + };
> + bool partial;
> + unsigned int algorithmformat;
> + char raw[EROFS_BLKSIZ];
> +
> + while (sum) {
> + end = offset + sum;
> + map.m_la = end - 1;
> +
> + ret = z_erofs_map_blocks_iter(vnode, &map);
> + if (ret)
> + return ret;
> +
> + if (!(map.m_flags & EROFS_MAP_MAPPED)) {
> + sum -= map.m_llen;
> + continue;
> + }
> +
> + ret = dev_read(raw, EROFS_BLKSIZ, map.m_pa);
> + if (ret < 0 || (size_t)ret != EROFS_BLKSIZ)
> + return -EIO;
> +
> + algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?
> + Z_EROFS_COMPRESSION_LZ4 :
> + Z_EROFS_COMPRESSION_SHIFTED;
> +
> + if (end >= map.m_la + map.m_llen) {
> + count = map.m_llen;
> + partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
> + } else {
> + count = end - map.m_la;
> + partial = true;
> + }
it would be better to make the logic more explicitly, like:
- if (end >= map.m_la + map.m_llen) {
- count = map.m_llen;
- partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
- } else {
+ /*
+ * 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) {
count = end - map.m_la;
partial = true;
+ } else {
+ ASSERT(end == map.m_la + map_m_llen);
+ count = map.m_llen;
+ partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
}
> +
> + if ((off_t)map.m_la < offset) {
> + ofs = offset - map.m_la;
> + sum = 0;
> + } else {
> + ofs = 0;
> + sum -= count;
> + }
a bit weird here, anyway, let me clean up here as well.
> +
> + ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
> + .in = raw,
> + .out = buffer,
> + .ofs_out = sum,
> + .ofs_head = ofs,
> + .inputsize = EROFS_BLKSIZ,
> + .outputsize = count,
> + .alg = algorithmformat,
> + .partial_decoding = partial
> + });
> + if (ret < 0)
> + return ret;
> + }
> +
> + logi("nid:%u size=%zd offset=%llu done",
> + vnode->nid, size, (long long)offset);
> + return size;
> +}
>
> int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
> struct fuse_file_info *fi)
> @@ -107,7 +175,8 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
>
> case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> case EROFS_INODE_FLAT_COMPRESSION:
> - /* Fixme: */
> + return erofs_read_data_compression(&v, buffer, size, offset);
> +
> default:
> return -EINVAL;
> }
> diff --git a/fuse/zmap.c b/fuse/zmap.c
> new file mode 100644
> index 0000000..022ca1b
> --- /dev/null
> +++ b/fuse/zmap.c
let's move this file to lib/ as well.
> @@ -0,0 +1,416 @@
> +// SPDX-License-Identifier: GPL-2.0-only
Let's relicense this file to GPL-2.0+ for erofs-utils
(since I'm the original author, and assume you agree on this as well..)
> +/*
> + * Many parts of codes are copied from Linux kernel/fs/erofs.
> + *
> + * 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 "init.h"
> +#include "disk_io.h"
> +#include "logging.h"
> +
> +int z_erofs_fill_inode(struct erofs_vnode *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->z_inited = true;
> + }
> +
> + return 0;
> +}
> +
> +static int z_erofs_fill_inode_lazy(struct erofs_vnode *vi)
> +{
> + int ret;
> + erofs_off_t pos;
> + struct z_erofs_map_header *h;
> + char buf[8];
> +
> + if (vi->z_inited)
> + return 0;
> +
> + DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
> +
> + pos = round_up(nid2addr(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
> +
> + ret = dev_read(buf, 8, pos);
> + if (ret < 0 && (uint32_t)ret != 8)
> + 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) {
> + loge("unknown compression format %u for nid %llu",
> + vi->z_algorithmtype[0], 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) {
> + loge("unsupported physical clusterbits %u for nid %llu",
> + vi->z_physical_clusterbits[0], vi->nid);
> + return -EOPNOTSUPP;
> + }
> +
> + vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits +
> + ((h->h_clusterbits >> 5) & 7);
> + vi->z_inited = true;
> +
> + return 0;
> +}
> +
> +struct z_erofs_maprecorder {
> + struct erofs_vnode *vnode;
> + 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 = dev_read(mpage, EROFS_BLKSIZ, blknr_to_addr(eblk));
> + if (ret < 0 && (uint32_t)ret != EROFS_BLKSIZ)
> + return -EIO;
> +
> + map->index = eblk;
> +
> + return 0;
> +}
> +
> +static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,
> + unsigned long lcn)
> +{
> + struct erofs_vnode *const vi = m->vnode;
> + const erofs_off_t ibase = nid2addr(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_vnode *const vi = m->vnode;
> + 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_vnode *const vi = m->vnode;
> + const unsigned int lclusterbits = vi->z_logical_clusterbits;
> + const erofs_off_t ebase = round_up(nid2addr(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->vnode->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_vnode *const vi = m->vnode;
> + 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) {
> + loge("bogus lookback distance @ nid %llu", 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]) {
> + loge("invalid lookback distance 0 @ nid %llu",
> + 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:
> + loge("unknown type %u @ lcn %lu of nid %llu",
> + m->type, lcn, vi->nid);
> + DBG_BUGON(1);
> + return -EOPNOTSUPP;
> + }
> + return 0;
> +}
> +
> +int z_erofs_map_blocks_iter(struct erofs_vnode *vi,
> + struct erofs_map_blocks *map)
> +{
> + struct z_erofs_maprecorder m = {
> + .vnode = 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) {
> + loge("invalid logical cluster 0 at nid %llu",
> + 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:
> + loge("unknown type %u @ offset %llu of nid %llu",
> + m.type, ofs, 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:
> + logd("m_la %llu m_pa %llu m_llen %llu m_plen %llu 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/include/erofs/defs.h b/include/erofs/defs.h
> index a9c769e..06d29a9 100644
> --- a/include/erofs/defs.h
> +++ b/include/erofs/defs.h
> @@ -173,5 +173,18 @@ typedef int64_t s64;
> #define __maybe_unused __attribute__((__unused__))
> #endif
>
> +struct __una_u32 { u32 x; } __packed;
> +
> +static inline u32 __get_unaligned_cpu32(const void *p)
> +{
> + const struct __una_u32 *ptr = (const struct __una_u32 *)p;
> + return ptr->x;
> +}
> +
> +static inline u32 get_unaligned_le32(const void *p)
> +{
> + return __get_unaligned_cpu32((const u8 *)p);
> +}
need to handle big-endian, anyway, minor for now.
Thanks,
Gao Xiang
[-- Attachment #2: Type: text/html, Size: 43219 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Re: [PATCH 5/5] erofs-utils: support read compressed file
2020-10-17 6:24 ` jnhuang95
@ 2020-10-17 6:36 ` Gao Xiang via Linux-erofs
0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang via Linux-erofs @ 2020-10-17 6:36 UTC (permalink / raw)
To: jnhuang95; +Cc: zhangshiming, linux-erofs, guoweichao
On Sat, Oct 17, 2020 at 02:24:13PM +0800, jnhuang95@gmail.com wrote:
> Hi Gaoxiang,
>
> Thanks for your review and suggestions. It's great to communicate with outstanding community developers. I have learned a lot and look forward to contribute more patches in the future.
Thanks for your hard effort on this. I've send out a complete version [1]
which I'm also working on and you can review / comment your own thoughts
about these as well. It's always welcomed to join us and send patches
for kernel & userspace side if you have extra time or production is
needed :) Anyway, our project needs more people to understand and look
after, so in that way it would make the whole project better!
[1] https://lore.kernel.org/r/20201017051621.7810-1-hsiangkao@aol.com
Thanks,
Gao Xiang
>
> Regards,
> Jianan
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2020-10-17 6:36 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-15 13:39 [PATCH 1/5] erofs-utils: fix the conflict with the master branch Huang Jianan
2020-10-15 13:39 ` [PATCH 2/5] erofs-utils: fix the wrong name length of tail file in the directory Huang Jianan
2020-10-15 13:39 ` [PATCH 3/5] erofs-utils: fix the wrong address of inline dir content Huang Jianan
2020-10-16 15:36 ` Gao Xiang via Linux-erofs
2020-10-15 13:39 ` [PATCH 4/5] erofs-utils: support read special file Huang Jianan
2020-10-15 13:39 ` [PATCH 5/5] erofs-utils: support read compressed file Huang Jianan
2020-10-16 16:17 ` Gao Xiang via Linux-erofs
2020-10-17 6:24 ` jnhuang95
2020-10-17 6:36 ` Gao Xiang via Linux-erofs
2020-10-16 15:41 ` [PATCH 1/5] erofs-utils: fix the conflict with the master branch Gao Xiang via Linux-erofs
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).