* [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
* 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
* [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 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
* 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
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).