Keep in sync with the latest kernel
commit 2a9dc7a8fec6 ("erofs: introduce chunk-based file on-disk format")
and
commit c5aa903a59db ("erofs: support reading chunk-based uncompressed files")

Signed-off-by: Gao Xiang <[email protected]>
---
 include/erofs/internal.h |  5 +++
 include/erofs_fs.h       | 48 ++++++++++++++++++++--
 lib/data.c               | 86 +++++++++++++++++++++++++++++++++++-----
 lib/namei.c              | 15 ++++++-
 4 files changed, 140 insertions(+), 14 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index f5eacea5d4d7..8621f3426410 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -109,6 +109,7 @@ static inline void erofs_sb_clear_##name(void) \
 EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING)
 EROFS_FEATURE_FUNCS(compr_cfgs, incompat, INCOMPAT_COMPR_CFGS)
 EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER)
+EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
 EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
 
 #define EROFS_I_EA_INITED      (1 << 0)
@@ -140,6 +141,10 @@ struct erofs_inode {
                u32 i_blkaddr;
                u32 i_blocks;
                u32 i_rdev;
+               struct {
+                       unsigned short  chunkformat;
+                       unsigned char   chunkbits;
+               };
        } u;
 
        char i_srcpath[PATH_MAX + 1];
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index 48934bb76cec..66a68e3b2065 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2017-2018 HUAWEI, Inc.
  *             http://www.huawei.com/
- * Created by Gao Xiang <[email protected]>
+ * Copyright (C) 2021, Alibaba Cloud
  */
 #ifndef __EROFS_FS_H
 #define __EROFS_FS_H
@@ -21,10 +21,12 @@
 #define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING    0x00000001
 #define EROFS_FEATURE_INCOMPAT_COMPR_CFGS      0x00000002
 #define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER    0x00000002
+#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE    0x00000004
 #define EROFS_ALL_FEATURE_INCOMPAT             \
        (EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | \
         EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
-        EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER)
+        EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER | \
+        EROFS_FEATURE_INCOMPAT_CHUNKED_FILE)
 
 #define EROFS_SB_EXTSLOT_SIZE  16
 
@@ -66,13 +68,16 @@ struct erofs_super_block {
  * inode, [xattrs], last_inline_data, ... | ... | no-holed data
  * 3 - inode compression D:
  * inode, [xattrs], map_header, extents ... | ...
- * 4~7 - reserved
+ * 4 - inode chunk-based E:
+ * inode, [xattrs], chunk indexes ... | ...
+ * 5~7 - reserved
  */
 enum {
        EROFS_INODE_FLAT_PLAIN                  = 0,
        EROFS_INODE_FLAT_COMPRESSION_LEGACY     = 1,
        EROFS_INODE_FLAT_INLINE                 = 2,
        EROFS_INODE_FLAT_COMPRESSION            = 3,
+       EROFS_INODE_CHUNK_BASED                 = 4,
        EROFS_INODE_DATALAYOUT_MAX
 };
 
@@ -92,6 +97,19 @@ static inline bool erofs_inode_is_data_compressed(unsigned 
int datamode)
 #define EROFS_I_ALL    \
        ((1 << (EROFS_I_DATALAYOUT_BIT + EROFS_I_DATALAYOUT_BITS)) - 1)
 
+/* indicate chunk blkbits, thus 'chunksize = blocksize << chunk blkbits' */
+#define EROFS_CHUNK_FORMAT_BLKBITS_MASK                0x001F
+/* with chunk indexes or just a 4-byte blkaddr array */
+#define EROFS_CHUNK_FORMAT_INDEXES             0x0020
+
+#define EROFS_CHUNK_FORMAT_ALL \
+       (EROFS_CHUNK_FORMAT_BLKBITS_MASK | EROFS_CHUNK_FORMAT_INDEXES)
+
+struct erofs_inode_chunk_info {
+       __le16 format;          /* chunk blkbits, etc. */
+       __le16 reserved;
+};
+
 /* 32-byte reduced form of an ondisk inode */
 struct erofs_inode_compact {
        __le16 i_format;        /* inode format hints */
@@ -109,6 +127,9 @@ struct erofs_inode_compact {
 
                /* for device files, used to indicate old/new device # */
                __le32 rdev;
+
+               /* for chunk-based files, it contains the summary info */
+               struct erofs_inode_chunk_info c;
        } i_u;
        __le32 i_ino;           /* only used for 32-bit stat compatibility */
        __le16 i_uid;
@@ -137,6 +158,9 @@ struct erofs_inode_extended {
 
                /* for device files, used to indicate old/new device # */
                __le32 rdev;
+
+               /* for chunk-based files, it contains the summary info */
+               struct erofs_inode_chunk_info c;
        } i_u;
 
        /* only used for 32-bit stat compatibility */
@@ -206,6 +230,19 @@ static inline unsigned int erofs_xattr_entry_size(struct 
erofs_xattr_entry *e)
                                 e->e_name_len + le16_to_cpu(e->e_value_size));
 }
 
+/* represent a zeroed chunk (hole) */
+#define EROFS_NULL_ADDR                        -1
+
+/* 4-byte block address array */
+#define EROFS_BLOCK_MAP_ENTRY_SIZE     sizeof(__le32)
+
+/* 8-byte inode chunk indexes */
+struct erofs_inode_chunk_index {
+       __le16 advise;          /* always 0, don't care for now */
+       __le16 device_id;       /* back-end storage id, always 0 for now */
+       __le32 blkaddr;         /* start block address of this inode chunk */
+};
+
 /* maximum supported size of a physical compression cluster */
 #define Z_EROFS_PCLUSTER_MAX_SIZE      (1024 * 1024)
 
@@ -350,9 +387,14 @@ static inline void 
erofs_check_ondisk_layout_definitions(void)
        BUILD_BUG_ON(sizeof(struct erofs_inode_extended) != 64);
        BUILD_BUG_ON(sizeof(struct erofs_xattr_ibody_header) != 12);
        BUILD_BUG_ON(sizeof(struct erofs_xattr_entry) != 4);
+       BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_info) != 4);
+       BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) != 8);
        BUILD_BUG_ON(sizeof(struct z_erofs_map_header) != 8);
        BUILD_BUG_ON(sizeof(struct z_erofs_vle_decompressed_index) != 8);
        BUILD_BUG_ON(sizeof(struct erofs_dirent) != 12);
+       /* keep in sync between 2 index structures for better extendibility */
+       BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) !=
+                    sizeof(struct z_erofs_vle_decompressed_index));
 
        BUILD_BUG_ON(BIT(Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) <
                     Z_EROFS_VLE_CLUSTER_TYPE_MAX - 1);
diff --git a/lib/data.c b/lib/data.c
index 1a1005a67350..641d8408b54f 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -25,13 +25,6 @@ static int erofs_map_blocks_flatmode(struct erofs_inode 
*inode,
        nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
        lastblk = nblocks - tailendpacking;
 
-       if (offset >= inode->i_size) {
-               /* leave out-of-bound access unmapped */
-               map->m_flags = 0;
-               map->m_plen = 0;
-               goto out;
-       }
-
        /* there is no hole in flatmode */
        map->m_flags = EROFS_MAP_MAPPED;
 
@@ -62,14 +55,86 @@ static int erofs_map_blocks_flatmode(struct erofs_inode 
*inode,
                goto err_out;
        }
 
-out:
        map->m_llen = map->m_plen;
-
 err_out:
        trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0);
        return err;
 }
 
+static int erofs_map_blocks(struct erofs_inode *inode,
+                           struct erofs_map_blocks *map, int flags)
+{
+       struct erofs_inode *vi = inode;
+       struct erofs_inode_chunk_index *idx;
+       u8 buf[EROFS_BLKSIZ];
+       u64 chunknr;
+       unsigned int unit;
+       erofs_off_t pos;
+       int err = 0;
+
+       if (map->m_la >= inode->i_size) {
+               /* leave out-of-bound access unmapped */
+               map->m_flags = 0;
+               map->m_plen = 0;
+               goto out;
+       }
+
+       if (vi->datalayout != EROFS_INODE_CHUNK_BASED)
+               return erofs_map_blocks_flatmode(inode, map, flags);
+
+       if (vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
+               unit = sizeof(*idx);                    /* chunk index */
+       else
+               unit = EROFS_BLOCK_MAP_ENTRY_SIZE;      /* block map */
+
+       chunknr = map->m_la >> vi->u.chunkbits;
+       pos = roundup(iloc(vi->nid) + vi->inode_isize +
+                     vi->xattr_isize, unit) + unit * chunknr;
+
+       err = blk_read(buf, erofs_blknr(pos), 1);
+       if (err < 0)
+               return -EIO;
+
+       map->m_la = chunknr << vi->u.chunkbits;
+       map->m_plen = min_t(erofs_off_t, 1UL << vi->u.chunkbits,
+                           roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));
+
+       /* handle block map */
+       if (!(vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
+               __le32 *blkaddr = (void *)buf + erofs_blkoff(pos);
+
+               if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
+                       map->m_flags = 0;
+               } else {
+                       map->m_pa = blknr_to_addr(le32_to_cpu(*blkaddr));
+                       map->m_flags = EROFS_MAP_MAPPED;
+               }
+               goto out;
+       }
+       /* parse chunk indexes */
+       idx = (void *)buf + erofs_blkoff(pos);
+       switch (le32_to_cpu(idx->blkaddr)) {
+       case EROFS_NULL_ADDR:
+               map->m_flags = 0;
+               break;
+       default:
+               /* only one device is supported for now */
+               if (idx->device_id) {
+                       erofs_err("invalid device id %u @ %" PRIu64 " for nid 
%llu",
+                                 le16_to_cpu(idx->device_id),
+                                 chunknr, vi->nid | 0ULL);
+                       err = -EFSCORRUPTED;
+                       goto out;
+               }
+               map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
+               map->m_flags = EROFS_MAP_MAPPED;
+               break;
+       }
+out:
+       map->m_llen = map->m_plen;
+       return err;
+}
+
 static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
                               erofs_off_t size, erofs_off_t offset)
 {
@@ -84,7 +149,7 @@ static int erofs_read_raw_data(struct erofs_inode *inode, 
char *buffer,
                erofs_off_t eend;
 
                map.m_la = ptr;
-               ret = erofs_map_blocks_flatmode(inode, &map, 0);
+               ret = erofs_map_blocks(inode, &map, 0);
                if (ret)
                        return ret;
 
@@ -206,6 +271,7 @@ int erofs_pread(struct erofs_inode *inode, char *buf,
        switch (inode->datalayout) {
        case EROFS_INODE_FLAT_PLAIN:
        case EROFS_INODE_FLAT_INLINE:
+       case EROFS_INODE_CHUNK_BASED:
                return erofs_read_raw_data(inode, buf, count, offset);
        case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
        case EROFS_INODE_FLAT_COMPRESSION:
diff --git a/lib/namei.c b/lib/namei.c
index f96e400c36b0..b4bdabf10acb 100644
--- a/lib/namei.c
+++ b/lib/namei.c
@@ -82,6 +82,9 @@ static int erofs_read_inode_from_disk(struct erofs_inode *vi)
                vi->i_ctime = le64_to_cpu(die->i_ctime);
                vi->i_ctime_nsec = le64_to_cpu(die->i_ctime_nsec);
                vi->i_size = le64_to_cpu(die->i_size);
+               if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
+                       /* fill chunked inode summary info */
+                       vi->u.chunkformat = le16_to_cpu(die->i_u.c.format);
                break;
        case EROFS_INODE_LAYOUT_COMPACT:
                vi->inode_isize = sizeof(struct erofs_inode_compact);
@@ -115,6 +118,8 @@ static int erofs_read_inode_from_disk(struct erofs_inode 
*vi)
                vi->i_ctime_nsec = sbi.build_time_nsec;
 
                vi->i_size = le32_to_cpu(dic->i_size);
+               if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
+                       vi->u.chunkformat = le16_to_cpu(dic->i_u.c.format);
                break;
        default:
                erofs_err("unsupported on-disk inode version %u of nid %llu",
@@ -123,7 +128,15 @@ static int erofs_read_inode_from_disk(struct erofs_inode 
*vi)
        }
 
        vi->flags = 0;
-       if (erofs_inode_is_data_compressed(vi->datalayout))
+       if (vi->datalayout == EROFS_INODE_CHUNK_BASED) {
+               if (vi->u.chunkformat & ~EROFS_CHUNK_FORMAT_ALL) {
+                       erofs_err("unsupported chunk format %x of nid %llu",
+                                 vi->u.chunkformat, vi->nid | 0ULL);
+                       return -EOPNOTSUPP;
+               }
+               vi->u.chunkbits = LOG_BLOCK_SIZE +
+                       (vi->u.chunkformat & EROFS_CHUNK_FORMAT_BLKBITS_MASK);
+       } else if (erofs_inode_is_data_compressed(vi->datalayout))
                z_erofs_fill_inode(vi);
        return 0;
 bogusimode:
-- 
2.24.4

Reply via email to