<html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><style>body { line-height: 1.5; }blockquote { margin-top: 0px; margin-bottom: 0px; margin-left: 0.5em; }body { font-size: 14px; font-family: 'Microsoft YaHei UI'; color: rgb(0, 0, 0); line-height: 1.5; }</style></head><body>
<div><span></span>Hi Gaoxiang,</div><div><span style="background-color: transparent;"><br></span></div><div><span style="background-color: transparent;">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.</span></div>
<div><br></div><div><span style="color: rgb(18, 18, 18); font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans CJK SC', 'WenQuanYi Micro Hei', sans-serif; font-size: 15px; font-variant-ligatures: normal; orphans: 2; widows: 2;">Regards,</span></div><div><span style="color: rgb(18, 18, 18); font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans CJK SC', 'WenQuanYi Micro Hei', sans-serif; font-size: 15px; font-variant-ligatures: normal; orphans: 2; widows: 2;">Jianan</span></div><hr style="width: 210px; height: 1px; display: none;" color="#b5c4df" size="1" align="left">
<div><span></span></div>
<blockquote style="margin-Top: 0px; margin-Bottom: 0px; margin-Left: 0.5em; margin-Right: inherit"><div> </div><div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm"><div style="PADDING-RIGHT: 8px; PADDING-LEFT: 8px; FONT-SIZE: 12px;FONT-FAMILY:tahoma;COLOR:#000000; BACKGROUND: #efefef; PADDING-BOTTOM: 8px; PADDING-TOP: 8px"><div><b>From:</b> <a href="mailto:hsiangkao@aol.com">Gao Xiang</a></div><div><b>Date:</b> 2020-10-17 00:17</div><div><b>To:</b> <a href="mailto:jnhuang95@gmail.com">Huang Jianan</a></div><div><b>CC:</b> <a href="mailto:linux-erofs@lists.ozlabs.org">linux-erofs</a>; <a href="mailto:guoweichao@oppo.com">guoweichao</a>; <a href="mailto:zhangshiming@oppo.com">zhangshiming</a>; <a href="mailto:huangjianan@oppo.com">huangjianan</a></div><div><b>Subject:</b> Re: [PATCH 5/5] erofs-utils: support read compressed file</div></div></div><div><div>Hi Jianan,</div>
<div> </div>
<div>On Thu, Oct 15, 2020 at 09:39:59PM +0800, Huang Jianan wrote:</div>
<div>> Signed-off-by: Huang Jianan <huangjianan@oppo.com></div>
<div>> Signed-off-by: Guo Weichao <guoweichao@oppo.com></div>
<div> </div>
<div>I read the logic below, and generally it looks good! <thumb></div>
<div> </div>
<div>I'm now handling some minor thing about this and I will</div>
<div>update as the following patch to speed up the development.</div>
<div> </div>
<div>> ---</div>
<div> </div>
<div>...</div>
<div> </div>
<div>> diff --git a/fuse/decompress.c b/fuse/decompress.c</div>
<div>> new file mode 100644</div>
<div>> index 0000000..e2df3ce</div>
<div>> --- /dev/null</div>
<div>> +++ b/fuse/decompress.c</div>
<div> </div>
<div>we might need to move this file to lib/decompress.c</div>
<div> </div>
<div>> @@ -0,0 +1,86 @@</div>
<div>> +/* SPDX-License-Identifier: GPL-2.0+ */</div>
<div>> +/*</div>
<div>> + * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.</div>
<div>> + * Created by Huang Jianan <huangjianan@oppo.com></div>
<div>> + */</div>
<div>> +</div>
<div>> +#include <stdlib.h></div>
<div>> +#include <lz4.h></div>
<div>> +</div>
<div>> +#include "erofs/internal.h"</div>
<div>> +#include "erofs/err.h"</div>
<div>> +#include "decompress.h"</div>
<div>> +#include "logging.h"</div>
<div>> +#include "init.h"</div>
<div>> +</div>
<div>> +static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq)</div>
<div>> +{</div>
<div>> +       char *dest = rq->out + rq->ofs_out;</div>
<div> </div>
<div>I think ofs_out can be omited, since we only care about the dest buffer here.</div>
<div>so (new) rq->out == (old)rq->out + rq->ofs_out.</div>
<div> </div>
<div>> +       char *src = rq->in + rq->ofs_head;</div>
<div> </div>
<div>need to judge if the outputsize is larger than EROFS_BLKSIZ, since it's</div>
<div>undefined on-disk behavior, and can be implemented in the future</div>
<div>on-disk update.</div>
<div> </div>
<div>> +</div>
<div>> +       memcpy(dest, src, rq->outputsize - rq->ofs_head);</div>
<div>> +</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div> </div>
<div>And this function can be folded into z_erofs_decompress(), because the</div>
<div>implementation here is much simple enough.</div>
<div> </div>
<div>> +</div>
<div>> +static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq)</div>
<div> </div>
<div>it might be renamed into z_erofs_decompress_lz4.</div>
<div> </div>
<div>> +{</div>
<div>> +       int ret = 0;</div>
<div>> +       char *dest = rq->out + rq->ofs_out;</div>
<div>> +       char *src = rq->in;</div>
<div>> +       char *buff = NULL;</div>
<div>> +       bool support_0padding = false;</div>
<div>> +       unsigned int inputmargin = 0;</div>
<div>> +</div>
<div>> +       if (sbk->feature_incompat & EROFS_FEATURE_INCOMPAT_LZ4_0PADDING) {</div>
<div>> +               support_0padding = true;</div>
<div>> +</div>
<div>> +               while (!src[inputmargin & ~PAGE_MASK])</div>
<div>> +                       if (!(++inputmargin & ~PAGE_MASK))</div>
<div>> +                               break;</div>
<div>> +</div>
<div>> +               if (inputmargin >= rq->inputsize)</div>
<div>> +                       return -EIO;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       if (rq->ofs_head) {</div>
<div> </div>
<div>we might need to rethink about the name of ofs_head.</div>
<div> </div>
<div>> +               buff = malloc(rq->outputsize);</div>
<div>> +               if (!buff)</div>
<div>> +                       return -ENOMEM;</div>
<div>> +               dest = buff;</div>
<div> </div>
<div>need some cleanup, and dest variable is assigned for several times,</div>
<div>but without making the logic simpler.</div>
<div> </div>
<div>> +       }</div>
<div>> +</div>
<div>> +       if (rq->partial_decoding || !support_0padding)</div>
<div>> +               ret = LZ4_decompress_safe_partial(src + inputmargin, dest,</div>
<div>> +                                                 rq->inputsize - inputmargin,</div>
<div>> +                                                 rq->outputsize, rq->outputsize);</div>
<div>> +       else</div>
<div>> +               ret = LZ4_decompress_safe(src + inputmargin, dest,</div>
<div>> +                                         rq->inputsize - inputmargin,</div>
<div>> +                                         rq->outputsize);</div>
<div>> +</div>
<div>> +       if (ret != (int)rq->outputsize) {</div>
<div>> +               ret = -EIO;</div>
<div>> +               goto out;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       if (rq->ofs_head) {</div>
<div>> +               src = dest + rq->ofs_head;</div>
<div>> +               dest = rq->out + rq->ofs_out;</div>
<div>> +               memcpy(dest, src, rq->outputsize - rq->ofs_head);</div>
<div>> +       }</div>
<div>> +</div>
<div>> +out:</div>
<div>> +       if (buff)</div>
<div>> +               free(buff);</div>
<div>> +</div>
<div>> +       return ret;</div>
<div>> +}</div>
<div>> +</div>
<div>> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)</div>
<div>> +{</div>
<div>> +       if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)</div>
<div>> +               return z_erofs_shifted_transform(rq);</div>
<div>> +</div>
<div>> +       return z_erofs_decompress_generic(rq);</div>
<div>> +}</div>
<div>> diff --git a/fuse/decompress.h b/fuse/decompress.h</div>
<div>> new file mode 100644</div>
<div>> index 0000000..cd395c3</div>
<div>> --- /dev/null</div>
<div>> +++ b/fuse/decompress.h</div>
<div> </div>
<div>move this file to include/erofs/decompress.h</div>
<div> </div>
<div>> @@ -0,0 +1,37 @@</div>
<div>> +/* SPDX-License-Identifier: GPL-2.0+ */</div>
<div>> +/*</div>
<div>> + * Copyright (C), 2008-2020, OPPO Mobile Comm Corp., Ltd.</div>
<div>> + * Created by Huang Jianan <huangjianan@oppo.com></div>
<div>> + */</div>
<div>> +</div>
<div>> +#ifndef __EROFS_DECOMPRESS_H</div>
<div>> +#define __EROFS_DECOMPRESS_H</div>
<div>> +</div>
<div>> +#include "erofs/internal.h"</div>
<div>> +</div>
<div>> +enum {</div>
<div>> +       Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,</div>
<div>> +       Z_EROFS_COMPRESSION_RUNTIME_MAX</div>
<div>> +};</div>
<div>> +</div>
<div>> +struct z_erofs_decompress_req {</div>
<div>> +       char *in, *out;</div>
<div>> +</div>
<div>> +       size_t ofs_out, ofs_head;</div>
<div>> +       unsigned int inputsize, outputsize;</div>
<div>> +</div>
<div>> +       /* indicate the algorithm will be used for decompression */</div>
<div>> +       unsigned int alg;</div>
<div>> +       bool partial_decoding;</div>
<div>> +};</div>
<div>> +</div>
<div>> +#ifdef LZ4_ENABLED</div>
<div>> +int z_erofs_decompress(struct z_erofs_decompress_req *rq);</div>
<div>> +#else</div>
<div>> +int z_erofs_decompress(struct z_erofs_decompress_req *rq)</div>
<div>> +{</div>
<div>> +       return -EOPNOTSUPP;</div>
<div>> +}</div>
<div>> +#endif</div>
<div>> +</div>
<div>> +#endif</div>
<div>> diff --git a/fuse/dentry.h b/fuse/dentry.h</div>
<div>> index ee2144d..f89c506 100644</div>
<div>> --- a/fuse/dentry.h</div>
<div>> +++ b/fuse/dentry.h</div>
<div>> @@ -10,10 +10,11 @@</div>
<div>>  #include <stdint.h></div>
<div>>  #include "erofs/internal.h"</div>
<div>>  </div>
<div>> +/* fixme: Deal with names that exceed the allocated size */</div>
<div>>  #ifdef __64BITS</div>
<div>> -#define DCACHE_ENTRY_NAME_LEN       40</div>
<div>> +#define DCACHE_ENTRY_NAME_LEN       EROFS_NAME_LEN</div>
<div>>  #else</div>
<div>> -#define DCACHE_ENTRY_NAME_LEN       48</div>
<div>> +#define DCACHE_ENTRY_NAME_LEN       EROFS_NAME_LEN</div>
<div>>  #endif</div>
<div> </div>
<div>not related to this patch, does it relate to another preexist</div>
<div>bug about erofsfuse codebase?</div>
<div> </div>
<div>>  </div>
<div>>  /* This struct declares a node of a k-tree.  Every node has a pointer to one of</div>
<div>> diff --git a/fuse/init.c b/fuse/init.c</div>
<div>> index 8198fa7..e9cc9f8 100644</div>
<div>> --- a/fuse/init.c</div>
<div>> +++ b/fuse/init.c</div>
<div>> @@ -17,7 +17,23 @@</div>
<div>>  </div>
<div>>  </div>
<div>>  struct erofs_super_block super;</div>
<div>> -static struct erofs_super_block *sbk = &super;</div>
<div>> +struct erofs_super_block *sbk = &super;</div>
<div>> +</div>
<div>> +static bool check_layout_compatibility(struct erofs_super_block *sb,</div>
<div>> +                                      struct erofs_super_block *dsb)</div>
<div>> +{</div>
<div>> +       const unsigned int feature = le32_to_cpu(dsb->feature_incompat);</div>
<div>> +</div>
<div>> +       sb->feature_incompat = feature;</div>
<div>> +</div>
<div>> +       /* check if current kernel meets all mandatory requirements */</div>
<div>> +       if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) {</div>
<div>> +               loge("unidentified incompatible feature %x, please upgrade kernel version",</div>
<div>> +                    feature & ~EROFS_ALL_FEATURE_INCOMPAT);</div>
<div>> +               return false;</div>
<div>> +       }</div>
<div>> +       return true;</div>
<div>> +}</div>
<div>>  </div>
<div>>  int erofs_init_super(void)</div>
<div>>  {</div>
<div>> @@ -40,6 +56,9 @@ int erofs_init_super(void)</div>
<div>>            return -EINVAL;</div>
<div>>    }</div>
<div>>  </div>
<div>> +       if (!check_layout_compatibility(sbk, sb))</div>
<div>> +               return -EINVAL;</div>
<div>> +</div>
<div>>    sbk->checksum = le32_to_cpu(sb->checksum);</div>
<div>>    sbk->feature_compat = le32_to_cpu(sb->feature_compat);</div>
<div>>    sbk->blkszbits = sb->blkszbits;</div>
<div>> @@ -56,6 +75,7 @@ int erofs_init_super(void)</div>
<div>>    sbk->root_nid = le16_to_cpu(sb->root_nid);</div>
<div>>  </div>
<div>>    logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));</div>
<div>> +       logp("%-15s:0x%X", STR(feature_incompat), SUPER_MEM(feature_incompat));</div>
<div>>    logp("%-15s:0x%X", STR(feature_compat), SUPER_MEM(feature_compat));</div>
<div>>    logp("%-15s:%u",   STR(blkszbits), SUPER_MEM(blkszbits));</div>
<div>>    logp("%-15s:%u",   STR(root_nid), SUPER_MEM(root_nid));</div>
<div>> diff --git a/fuse/init.h b/fuse/init.h</div>
<div>> index d7a97b5..3fc4eb5 100644</div>
<div>> --- a/fuse/init.h</div>
<div>> +++ b/fuse/init.h</div>
<div>> @@ -13,6 +13,8 @@</div>
<div>>  </div>
<div>>  #define BOOT_SECTOR_SIZE  0x400</div>
<div>>  </div>
<div>> +extern struct erofs_super_block *sbk;</div>
<div>> +</div>
<div>>  int erofs_init_super(void);</div>
<div>>  erofs_nid_t erofs_get_root_nid(void);</div>
<div>>  erofs_off_t nid2addr(erofs_nid_t nid);</div>
<div>> diff --git a/fuse/namei.c b/fuse/namei.c</div>
<div>> index 7ed1168..510fcfd 100644</div>
<div>> --- a/fuse/namei.c</div>
<div>> +++ b/fuse/namei.c</div>
<div>> @@ -49,7 +49,7 @@ static inline dev_t new_decode_dev(u32 dev)</div>
<div>>  </div>
<div>>  int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)</div>
<div>>  {</div>
<div>> -       int ret;</div>
<div>> +       int ret, ifmt;</div>
<div>>    char buf[EROFS_BLKSIZ];</div>
<div>>    struct erofs_inode_compact *v1;</div>
<div>>    const erofs_off_t addr = nid2addr(nid);</div>
<div>> @@ -60,6 +60,11 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)</div>
<div>>            return -EIO;</div>
<div>>  </div>
<div>>    v1 = (struct erofs_inode_compact *)buf;</div>
<div>> +       /* fixme: support extended inode */</div>
<div>> +       ifmt = le16_to_cpu(v1->i_format);</div>
<div>> +       if (__inode_version(ifmt) != EROFS_INODE_LAYOUT_COMPACT)</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +</div>
<div>>    vi->datalayout = __inode_data_mapping(le16_to_cpu(v1->i_format));</div>
<div>>    vi->inode_isize = sizeof(struct erofs_inode_compact);</div>
<div>>    vi->xattr_isize = erofs_xattr_ibody_size(v1->i_xattr_icount);</div>
<div>> @@ -88,6 +93,10 @@ int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)</div>
<div>>            return -EIO;</div>
<div>>    }</div>
<div>>  </div>
<div>> +       vi->z_inited = false;</div>
<div>> +       if (erofs_inode_is_data_compressed(vi->datalayout))</div>
<div>> +               z_erofs_fill_inode(vi);</div>
<div>> +</div>
<div>>    return 0;</div>
<div>>  }</div>
<div>>  </div>
<div>> diff --git a/fuse/read.c b/fuse/read.c</div>
<div>> index 3ce5c4f..cc0781f 100644</div>
<div>> --- a/fuse/read.c</div>
<div>> +++ b/fuse/read.c</div>
<div>> @@ -16,6 +16,7 @@</div>
<div>>  #include "namei.h"</div>
<div>>  #include "disk_io.h"</div>
<div>>  #include "init.h"</div>
<div>> +#include "decompress.h"</div>
<div>>  </div>
<div>>  size_t erofs_read_data(struct erofs_vnode *vnode, char *buffer,</div>
<div>>                   size_t size, off_t offset)</div>
<div>> @@ -78,6 +79,73 @@ finished:</div>
<div>>  </div>
<div>>  }</div>
<div>>  </div>
<div>> +size_t erofs_read_data_compression(struct erofs_vnode *vnode, char *buffer,</div>
<div>> +                      size_t size, off_t offset)</div>
<div>> +{</div>
<div>> +       int ret;</div>
<div>> +       size_t end, count, ofs, sum = size;</div>
<div>> +       struct erofs_map_blocks map = {</div>
<div>> +               .index = UINT_MAX,</div>
<div>> +       };</div>
<div>> +       bool partial;</div>
<div>> +       unsigned int algorithmformat;</div>
<div>> +       char raw[EROFS_BLKSIZ];</div>
<div>> +</div>
<div>> +       while (sum) {</div>
<div>> +               end = offset + sum;</div>
<div>> +               map.m_la = end - 1;</div>
<div>> +</div>
<div>> +               ret = z_erofs_map_blocks_iter(vnode, &map);</div>
<div>> +               if (ret)</div>
<div>> +                       return ret;</div>
<div>> +</div>
<div>> +               if (!(map.m_flags & EROFS_MAP_MAPPED)) {</div>
<div>> +                       sum -= map.m_llen;</div>
<div>> +                       continue;</div>
<div>> +               }</div>
<div>> +</div>
<div>> +               ret = dev_read(raw, EROFS_BLKSIZ, map.m_pa);</div>
<div>> +               if (ret < 0 || (size_t)ret != EROFS_BLKSIZ)</div>
<div>> +                       return -EIO;</div>
<div>> +</div>
<div>> +               algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?</div>
<div>> +                                               Z_EROFS_COMPRESSION_LZ4 :</div>
<div>> +                                               Z_EROFS_COMPRESSION_SHIFTED;</div>
<div>> +</div>
<div>> +               if (end >= map.m_la + map.m_llen) {</div>
<div>> +                       count = map.m_llen;</div>
<div>> +                       partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);</div>
<div>> +               } else {</div>
<div>> +                       count = end - map.m_la;</div>
<div>> +                       partial = true;</div>
<div>> +               }</div>
<div> </div>
<div>it would be better to make the logic more explicitly, like:</div>
<div> </div>
<div>-               if (end >= map.m_la + map.m_llen) {</div>
<div>-                       count = map.m_llen;</div>
<div>-                       partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);</div>
<div>-               } else {</div>
<div>+               /*</div>
<div>+                * trim to the needed size if the returned extent is quite</div>
<div>+                * larger than requested, and set up partial flag as well.</div>
<div>+                */</div>
<div>+               if (end < map.m_la + map.m_llen) {</div>
<div>                        count = end - map.m_la;</div>
<div>                        partial = true;</div>
<div>+               } else {</div>
<div>+                       ASSERT(end == map.m_la + map_m_llen);</div>
<div>+                       count = map.m_llen;</div>
<div>+                       partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);</div>
<div>                }</div>
<div> </div>
<div>> +</div>
<div>> +               if ((off_t)map.m_la < offset) {</div>
<div>> +                       ofs = offset - map.m_la;</div>
<div>> +                       sum = 0;</div>
<div>> +               } else {</div>
<div>> +                       ofs = 0;</div>
<div>> +                       sum -= count;</div>
<div>> +               }</div>
<div> </div>
<div>a bit weird here, anyway, let me clean up here as well.</div>
<div> </div>
<div>> +</div>
<div>> +               ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {</div>
<div>> +                                       .in = raw,</div>
<div>> +                                       .out = buffer,</div>
<div>> +                                       .ofs_out = sum,</div>
<div>> +                                       .ofs_head = ofs,</div>
<div>> +                                       .inputsize = EROFS_BLKSIZ,</div>
<div>> +                                       .outputsize = count,</div>
<div>> +                                       .alg = algorithmformat,</div>
<div>> +                                       .partial_decoding = partial</div>
<div>> +                                        });</div>
<div>> +               if (ret < 0)</div>
<div>> +                       return ret;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       logi("nid:%u size=%zd offset=%llu done",</div>
<div>> +            vnode->nid, size, (long long)offset);</div>
<div>> +       return size;</div>
<div>> +}</div>
<div>>  </div>
<div>>  int erofs_read(const char *path, char *buffer, size_t size, off_t offset,</div>
<div>>           struct fuse_file_info *fi)</div>
<div>> @@ -107,7 +175,8 @@ int erofs_read(const char *path, char *buffer, size_t size, off_t offset,</div>
<div>>  </div>
<div>>    case EROFS_INODE_FLAT_COMPRESSION_LEGACY:</div>
<div>>    case EROFS_INODE_FLAT_COMPRESSION:</div>
<div>> -               /* Fixme: */</div>
<div>> +               return erofs_read_data_compression(&v, buffer, size, offset);</div>
<div>> +</div>
<div>>    default:</div>
<div>>            return -EINVAL;</div>
<div>>    }</div>
<div>> diff --git a/fuse/zmap.c b/fuse/zmap.c</div>
<div>> new file mode 100644</div>
<div>> index 0000000..022ca1b</div>
<div>> --- /dev/null</div>
<div>> +++ b/fuse/zmap.c</div>
<div> </div>
<div>let's move this file to lib/ as well.</div>
<div> </div>
<div>> @@ -0,0 +1,416 @@</div>
<div>> +// SPDX-License-Identifier: GPL-2.0-only</div>
<div> </div>
<div>Let's relicense this file to GPL-2.0+ for erofs-utils</div>
<div>(since I'm the original author, and assume you agree on this as well..)</div>
<div> </div>
<div>> +/*</div>
<div>> + * Many parts of codes are copied from Linux kernel/fs/erofs.</div>
<div>> + *</div>
<div>> + * Copyright (C) 2018-2019 HUAWEI, Inc.</div>
<div>> + *             https://www.huawei.com/</div>
<div>> + * Created by Gao Xiang <gaoxiang25@huawei.com></div>
<div>> + * Modified by Huang Jianan <huangjianan@oppo.com></div>
<div>> + */</div>
<div>> +</div>
<div>> +#include "init.h"</div>
<div>> +#include "disk_io.h"</div>
<div>> +#include "logging.h"</div>
<div>> +</div>
<div>> +int z_erofs_fill_inode(struct erofs_vnode *vi)</div>
<div>> +{</div>
<div>> +       if (vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY) {</div>
<div>> +               vi->z_advise = 0;</div>
<div>> +               vi->z_algorithmtype[0] = 0;</div>
<div>> +               vi->z_algorithmtype[1] = 0;</div>
<div>> +               vi->z_logical_clusterbits = LOG_BLOCK_SIZE;</div>
<div>> +               vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits;</div>
<div>> +               vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits;</div>
<div>> +               vi->z_inited = true;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int z_erofs_fill_inode_lazy(struct erofs_vnode *vi)</div>
<div>> +{</div>
<div>> +       int ret;</div>
<div>> +       erofs_off_t pos;</div>
<div>> +       struct z_erofs_map_header *h;</div>
<div>> +       char buf[8];</div>
<div>> +</div>
<div>> +       if (vi->z_inited)</div>
<div>> +               return 0;</div>
<div>> +</div>
<div>> +       DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);</div>
<div>> +</div>
<div>> +       pos = round_up(nid2addr(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);</div>
<div>> +</div>
<div>> +       ret = dev_read(buf, 8, pos);</div>
<div>> +       if (ret < 0 && (uint32_t)ret != 8)</div>
<div>> +               return -EIO;</div>
<div>> +</div>
<div>> +       h = (struct z_erofs_map_header *)buf;</div>
<div>> +       vi->z_advise = le16_to_cpu(h->h_advise);</div>
<div>> +       vi->z_algorithmtype[0] = h->h_algorithmtype & 15;</div>
<div>> +       vi->z_algorithmtype[1] = h->h_algorithmtype >> 4;</div>
<div>> +</div>
<div>> +       if (vi->z_algorithmtype[0] >= Z_EROFS_COMPRESSION_MAX) {</div>
<div>> +               loge("unknown compression format %u for nid %llu",</div>
<div>> +                    vi->z_algorithmtype[0], vi->nid);</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       vi->z_logical_clusterbits = LOG_BLOCK_SIZE + (h->h_clusterbits & 7);</div>
<div>> +       vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits +</div>
<div>> +                                       ((h->h_clusterbits >> 3) & 3);</div>
<div>> +</div>
<div>> +       if (vi->z_physical_clusterbits[0] != LOG_BLOCK_SIZE) {</div>
<div>> +               loge("unsupported physical clusterbits %u for nid %llu",</div>
<div>> +                    vi->z_physical_clusterbits[0], vi->nid);</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits +</div>
<div>> +                                       ((h->h_clusterbits >> 5) & 7);</div>
<div>> +       vi->z_inited = true;</div>
<div>> +</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +struct z_erofs_maprecorder {</div>
<div>> +       struct erofs_vnode *vnode;</div>
<div>> +       struct erofs_map_blocks *map;</div>
<div>> +       void *kaddr;</div>
<div>> +</div>
<div>> +       unsigned long lcn;</div>
<div>> +       /* compression extent information gathered */</div>
<div>> +       u8  type;</div>
<div>> +       u16 clusterofs;</div>
<div>> +       u16 delta[2];</div>
<div>> +       erofs_blk_t pblk;</div>
<div>> +};</div>
<div>> +</div>
<div>> +static int z_erofs_reload_indexes(struct z_erofs_maprecorder *m,</div>
<div>> +                                 erofs_blk_t eblk)</div>
<div>> +{</div>
<div>> +       int ret;</div>
<div>> +       struct erofs_map_blocks *const map = m->map;</div>
<div>> +       char *mpage = map->mpage;</div>
<div>> +</div>
<div>> +       if (map->index == eblk)</div>
<div>> +               return 0;</div>
<div>> +</div>
<div>> +       ret = dev_read(mpage, EROFS_BLKSIZ, blknr_to_addr(eblk));</div>
<div>> +       if (ret < 0 && (uint32_t)ret != EROFS_BLKSIZ)</div>
<div>> +               return -EIO;</div>
<div>> +</div>
<div>> +       map->index = eblk;</div>
<div>> +</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,</div>
<div>> +                                        unsigned long lcn)</div>
<div>> +{</div>
<div>> +       struct erofs_vnode *const vi = m->vnode;</div>
<div>> +       const erofs_off_t ibase = nid2addr(vi->nid);</div>
<div>> +       const erofs_off_t pos =</div>
<div>> +               Z_EROFS_VLE_LEGACY_INDEX_ALIGN(ibase + vi->inode_isize +</div>
<div>> +                                              vi->xattr_isize) +</div>
<div>> +               lcn * sizeof(struct z_erofs_vle_decompressed_index);</div>
<div>> +       struct z_erofs_vle_decompressed_index *di;</div>
<div>> +       unsigned int advise, type;</div>
<div>> +       int err;</div>
<div>> +</div>
<div>> +       err = z_erofs_reload_indexes(m, erofs_blknr(pos));</div>
<div>> +       if (err)</div>
<div>> +               return err;</div>
<div>> +</div>
<div>> +       m->lcn = lcn;</div>
<div>> +       di = m->kaddr + erofs_blkoff(pos);</div>
<div>> +</div>
<div>> +       advise = le16_to_cpu(di->di_advise);</div>
<div>> +       type = (advise >> Z_EROFS_VLE_DI_CLUSTER_TYPE_BIT) &</div>
<div>> +               ((1 << Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) - 1);</div>
<div>> +       switch (type) {</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:</div>
<div>> +               m->clusterofs = 1 << vi->z_logical_clusterbits;</div>
<div>> +               m->delta[0] = le16_to_cpu(di->di_u.delta[0]);</div>
<div>> +               m->delta[1] = le16_to_cpu(di->di_u.delta[1]);</div>
<div>> +               break;</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:</div>
<div>> +               m->clusterofs = le16_to_cpu(di->di_clusterofs);</div>
<div>> +               m->pblk = le32_to_cpu(di->di_u.blkaddr);</div>
<div>> +               break;</div>
<div>> +       default:</div>
<div>> +               DBG_BUGON(1);</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +       }</div>
<div>> +       m->type = type;</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static unsigned int decode_compactedbits(unsigned int lobits,</div>
<div>> +                                        unsigned int lomask,</div>
<div>> +                                        u8 *in, unsigned int pos, u8 *type)</div>
<div>> +{</div>
<div>> +       const unsigned int v = get_unaligned_le32(in + pos / 8) >> (pos & 7);</div>
<div>> +       const unsigned int lo = v & lomask;</div>
<div>> +</div>
<div>> +       *type = (v >> lobits) & 3;</div>
<div>> +       return lo;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int unpack_compacted_index(struct z_erofs_maprecorder *m,</div>
<div>> +                                 unsigned int amortizedshift,</div>
<div>> +                                 unsigned int eofs)</div>
<div>> +{</div>
<div>> +       struct erofs_vnode *const vi = m->vnode;</div>
<div>> +       const unsigned int lclusterbits = vi->z_logical_clusterbits;</div>
<div>> +       const unsigned int lomask = (1 << lclusterbits) - 1;</div>
<div>> +       unsigned int vcnt, base, lo, encodebits, nblk;</div>
<div>> +       int i;</div>
<div>> +       u8 *in, type;</div>
<div>> +</div>
<div>> +       if (1 << amortizedshift == 4)</div>
<div>> +               vcnt = 2;</div>
<div>> +       else if (1 << amortizedshift == 2 && lclusterbits == 12)</div>
<div>> +               vcnt = 16;</div>
<div>> +       else</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +</div>
<div>> +       encodebits = ((vcnt << amortizedshift) - sizeof(__le32)) * 8 / vcnt;</div>
<div>> +       base = round_down(eofs, vcnt << amortizedshift);</div>
<div>> +       in = m->kaddr + base;</div>
<div>> +</div>
<div>> +       i = (eofs - base) >> amortizedshift;</div>
<div>> +</div>
<div>> +       lo = decode_compactedbits(lclusterbits, lomask,</div>
<div>> +                                 in, encodebits * i, &type);</div>
<div>> +       m->type = type;</div>
<div>> +       if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {</div>
<div>> +               m->clusterofs = 1 << lclusterbits;</div>
<div>> +               if (i + 1 != (int)vcnt) {</div>
<div>> +                       m->delta[0] = lo;</div>
<div>> +                       return 0;</div>
<div>> +               }</div>
<div>> +               /*</div>
<div>> +                * since the last lcluster in the pack is special,</div>
<div>> +                * of which lo saves delta[1] rather than delta[0].</div>
<div>> +                * Hence, get delta[0] by the previous lcluster indirectly.</div>
<div>> +                */</div>
<div>> +               lo = decode_compactedbits(lclusterbits, lomask,</div>
<div>> +                                         in, encodebits * (i - 1), &type);</div>
<div>> +               if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)</div>
<div>> +                       lo = 0;</div>
<div>> +               m->delta[0] = lo + 1;</div>
<div>> +               return 0;</div>
<div>> +       }</div>
<div>> +       m->clusterofs = lo;</div>
<div>> +       m->delta[0] = 0;</div>
<div>> +       /* figout out blkaddr (pblk) for HEAD lclusters */</div>
<div>> +       nblk = 1;</div>
<div>> +       while (i > 0) {</div>
<div>> +               --i;</div>
<div>> +               lo = decode_compactedbits(lclusterbits, lomask,</div>
<div>> +                                         in, encodebits * i, &type);</div>
<div>> +               if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)</div>
<div>> +                       i -= lo;</div>
<div>> +</div>
<div>> +               if (i >= 0)</div>
<div>> +                       ++nblk;</div>
<div>> +       }</div>
<div>> +       in += (vcnt << amortizedshift) - sizeof(__le32);</div>
<div>> +       m->pblk = le32_to_cpu(*(__le32 *)in) + nblk;</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,</div>
<div>> +                                           unsigned long lcn)</div>
<div>> +{</div>
<div>> +       struct erofs_vnode *const vi = m->vnode;</div>
<div>> +       const unsigned int lclusterbits = vi->z_logical_clusterbits;</div>
<div>> +       const erofs_off_t ebase = round_up(nid2addr(vi->nid) + vi->inode_isize +</div>
<div>> +                                          vi->xattr_isize, 8) +</div>
<div>> +               sizeof(struct z_erofs_map_header);</div>
<div>> +       const unsigned int totalidx = DIV_ROUND_UP(vi->i_size, EROFS_BLKSIZ);</div>
<div>> +       unsigned int compacted_4b_initial, compacted_2b;</div>
<div>> +       unsigned int amortizedshift;</div>
<div>> +       erofs_off_t pos;</div>
<div>> +       int err;</div>
<div>> +</div>
<div>> +       if (lclusterbits != 12)</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +</div>
<div>> +       if (lcn >= totalidx)</div>
<div>> +               return -EINVAL;</div>
<div>> +</div>
<div>> +       m->lcn = lcn;</div>
<div>> +       /* used to align to 32-byte (compacted_2b) alignment */</div>
<div>> +       compacted_4b_initial = (32 - ebase % 32) / 4;</div>
<div>> +       if (compacted_4b_initial == 32 / 4)</div>
<div>> +               compacted_4b_initial = 0;</div>
<div>> +</div>
<div>> +       if (vi->z_advise & Z_EROFS_ADVISE_COMPACTED_2B)</div>
<div>> +               compacted_2b = rounddown(totalidx - compacted_4b_initial, 16);</div>
<div>> +       else</div>
<div>> +               compacted_2b = 0;</div>
<div>> +</div>
<div>> +       pos = ebase;</div>
<div>> +       if (lcn < compacted_4b_initial) {</div>
<div>> +               amortizedshift = 2;</div>
<div>> +               goto out;</div>
<div>> +       }</div>
<div>> +       pos += compacted_4b_initial * 4;</div>
<div>> +       lcn -= compacted_4b_initial;</div>
<div>> +</div>
<div>> +       if (lcn < compacted_2b) {</div>
<div>> +               amortizedshift = 1;</div>
<div>> +               goto out;</div>
<div>> +       }</div>
<div>> +       pos += compacted_2b * 2;</div>
<div>> +       lcn -= compacted_2b;</div>
<div>> +       amortizedshift = 2;</div>
<div>> +out:</div>
<div>> +       pos += lcn * (1 << amortizedshift);</div>
<div>> +       err = z_erofs_reload_indexes(m, erofs_blknr(pos));</div>
<div>> +       if (err)</div>
<div>> +               return err;</div>
<div>> +       return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos));</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,</div>
<div>> +                                         unsigned int lcn)</div>
<div>> +{</div>
<div>> +       const unsigned int datamode = m->vnode->datalayout;</div>
<div>> +</div>
<div>> +       if (datamode == EROFS_INODE_FLAT_COMPRESSION_LEGACY)</div>
<div>> +               return legacy_load_cluster_from_disk(m, lcn);</div>
<div>> +</div>
<div>> +       if (datamode == EROFS_INODE_FLAT_COMPRESSION)</div>
<div>> +               return compacted_load_cluster_from_disk(m, lcn);</div>
<div>> +</div>
<div>> +       return -EINVAL;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,</div>
<div>> +                                  unsigned int lookback_distance)</div>
<div>> +{</div>
<div>> +       struct erofs_vnode *const vi = m->vnode;</div>
<div>> +       struct erofs_map_blocks *const map = m->map;</div>
<div>> +       const unsigned int lclusterbits = vi->z_logical_clusterbits;</div>
<div>> +       unsigned long lcn = m->lcn;</div>
<div>> +       int err;</div>
<div>> +</div>
<div>> +       if (lcn < lookback_distance) {</div>
<div>> +               loge("bogus lookback distance @ nid %llu", vi->nid);</div>
<div>> +               DBG_BUGON(1);</div>
<div>> +               return -EFSCORRUPTED;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       /* load extent head logical cluster if needed */</div>
<div>> +       lcn -= lookback_distance;</div>
<div>> +       err = z_erofs_load_cluster_from_disk(m, lcn);</div>
<div>> +       if (err)</div>
<div>> +               return err;</div>
<div>> +</div>
<div>> +       switch (m->type) {</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:</div>
<div>> +               if (!m->delta[0]) {</div>
<div>> +                       loge("invalid lookback distance 0 @ nid %llu",</div>
<div>> +                                 vi->nid);</div>
<div>> +                       DBG_BUGON(1);</div>
<div>> +                       return -EFSCORRUPTED;</div>
<div>> +               }</div>
<div>> +               return z_erofs_extent_lookback(m, m->delta[0]);</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:</div>
<div>> +               map->m_flags &= ~EROFS_MAP_ZIPPED;</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:</div>
<div>> +               map->m_la = (lcn << lclusterbits) | m->clusterofs;</div>
<div>> +               break;</div>
<div>> +       default:</div>
<div>> +               loge("unknown type %u @ lcn %lu of nid %llu",</div>
<div>> +                    m->type, lcn, vi->nid);</div>
<div>> +               DBG_BUGON(1);</div>
<div>> +               return -EOPNOTSUPP;</div>
<div>> +       }</div>
<div>> +       return 0;</div>
<div>> +}</div>
<div>> +</div>
<div>> +int z_erofs_map_blocks_iter(struct erofs_vnode *vi,</div>
<div>> +                           struct erofs_map_blocks *map)</div>
<div>> +{</div>
<div>> +       struct z_erofs_maprecorder m = {</div>
<div>> +               .vnode = vi,</div>
<div>> +               .map = map,</div>
<div>> +               .kaddr = map->mpage,</div>
<div>> +       };</div>
<div>> +       int err = 0;</div>
<div>> +       unsigned int lclusterbits, endoff;</div>
<div>> +       unsigned long long ofs, end;</div>
<div>> +</div>
<div>> +       /* when trying to read beyond EOF, leave it unmapped */</div>
<div>> +       if (map->m_la >= vi->i_size) {</div>
<div>> +               map->m_llen = map->m_la + 1 - vi->i_size;</div>
<div>> +               map->m_la = vi->i_size;</div>
<div>> +               map->m_flags = 0;</div>
<div>> +               goto out;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       err = z_erofs_fill_inode_lazy(vi);</div>
<div>> +       if (err)</div>
<div>> +               goto out;</div>
<div>> +</div>
<div>> +       lclusterbits = vi->z_logical_clusterbits;</div>
<div>> +       ofs = map->m_la;</div>
<div>> +       m.lcn = ofs >> lclusterbits;</div>
<div>> +       endoff = ofs & ((1 << lclusterbits) - 1);</div>
<div>> +</div>
<div>> +       err = z_erofs_load_cluster_from_disk(&m, m.lcn);</div>
<div>> +       if (err)</div>
<div>> +               goto out;</div>
<div>> +</div>
<div>> +       map->m_flags = EROFS_MAP_ZIPPED;     /* by default, compressed */</div>
<div>> +       end = (m.lcn + 1ULL) << lclusterbits;</div>
<div>> +       switch (m.type) {</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:</div>
<div>> +               if (endoff >= m.clusterofs)</div>
<div>> +                       map->m_flags &= ~EROFS_MAP_ZIPPED;</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:</div>
<div>> +               if (endoff >= m.clusterofs) {</div>
<div>> +                       map->m_la = (m.lcn << lclusterbits) | m.clusterofs;</div>
<div>> +                       break;</div>
<div>> +               }</div>
<div>> +               /* m.lcn should be >= 1 if endoff < m.clusterofs */</div>
<div>> +               if (!m.lcn) {</div>
<div>> +                       loge("invalid logical cluster 0 at nid %llu",</div>
<div>> +                            vi->nid);</div>
<div>> +                       err = -EFSCORRUPTED;</div>
<div>> +                       goto out;</div>
<div>> +               }</div>
<div>> +               end = (m.lcn << lclusterbits) | m.clusterofs;</div>
<div>> +               map->m_flags |= EROFS_MAP_FULL_MAPPED;</div>
<div>> +               m.delta[0] = 1;</div>
<div>> +       case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:</div>
<div>> +               /* get the correspoinding first chunk */</div>
<div>> +               err = z_erofs_extent_lookback(&m, m.delta[0]);</div>
<div>> +               if (err)</div>
<div>> +                       goto out;</div>
<div>> +               break;</div>
<div>> +       default:</div>
<div>> +               loge("unknown type %u @ offset %llu of nid %llu",</div>
<div>> +                    m.type, ofs, vi->nid);</div>
<div>> +               err = -EOPNOTSUPP;</div>
<div>> +               goto out;</div>
<div>> +       }</div>
<div>> +</div>
<div>> +       map->m_llen = end - map->m_la;</div>
<div>> +       map->m_plen = 1 << lclusterbits;</div>
<div>> +       map->m_pa = blknr_to_addr(m.pblk);</div>
<div>> +       map->m_flags |= EROFS_MAP_MAPPED;</div>
<div>> +</div>
<div>> +out:</div>
<div>> +       logd("m_la %llu m_pa %llu m_llen %llu m_plen %llu m_flags 0%o",</div>
<div>> +            map->m_la, map->m_pa,</div>
<div>> +            map->m_llen, map->m_plen, map->m_flags);</div>
<div>> +</div>
<div>> +       DBG_BUGON(err < 0 && err != -ENOMEM);</div>
<div>> +       return err;</div>
<div>> +}</div>
<div>> diff --git a/include/erofs/defs.h b/include/erofs/defs.h</div>
<div>> index a9c769e..06d29a9 100644</div>
<div>> --- a/include/erofs/defs.h</div>
<div>> +++ b/include/erofs/defs.h</div>
<div>> @@ -173,5 +173,18 @@ typedef int64_t         s64;</div>
<div>>  #define __maybe_unused      __attribute__((__unused__))</div>
<div>>  #endif</div>
<div>>  </div>
<div>> +struct __una_u32 { u32 x; } __packed;</div>
<div>> +</div>
<div>> +static inline u32 __get_unaligned_cpu32(const void *p)</div>
<div>> +{</div>
<div>> +       const struct __una_u32 *ptr = (const struct __una_u32 *)p;</div>
<div>> +       return ptr->x;</div>
<div>> +}</div>
<div>> +</div>
<div>> +static inline u32 get_unaligned_le32(const void *p)</div>
<div>> +{</div>
<div>> +       return __get_unaligned_cpu32((const u8 *)p);</div>
<div>> +}</div>
<div> </div>
<div>need to handle big-endian, anyway, minor for now.</div>
<div> </div>
<div>Thanks,</div>
<div>Gao Xiang</div>
<div> </div>
</div></blockquote>
</body></html>