test code v1

This commit is contained in:
2026-08-13 10:48:49 +02:00
parent f3b1165f19
commit f6aeadb22d
300 changed files with 0 additions and 31306 deletions
+928
View File
@@ -0,0 +1,928 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018-2019 HUAWEI, Inc.
* https://www.huawei.com/
*/
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include "internal.h"
struct z_erofs_maprecorder {
struct erofs_mount *em;
struct erofs_node *en;
struct erofs_map_blocks *map;
uint64_t lcn;
uint8_t type;
uint8_t headtype;
unsigned int clusterofs;
uint16_t delta[2];
erofs_blk_t pblk;
erofs_blk_t compressedblks;
erofs_off_t nextpackoff;
bool partialref;
};
static int
z_erofs_read_index(struct z_erofs_maprecorder *m, uint64_t pos, size_t len,
void **bufp)
{
return (erofs_read_metadata(m->em, m->en->nid, pos, len, bufp));
}
static int
z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m, uint64_t lcn)
{
struct erofs_node *en;
struct z_erofs_lcluster_index *di;
uint64_t base, pos;
unsigned int advise;
void *buf;
int error;
en = m->en;
base = en->inode_off + en->inode_isize + en->xattr_isize;
if (base < en->inode_off)
return (EOVERFLOW);
base = Z_EROFS_FULL_INDEX_START(base);
if (lcn > (UINT64_MAX - base) / sizeof(*di))
return (EOVERFLOW);
pos = base + lcn * sizeof(*di);
error = z_erofs_read_index(m, pos, sizeof(*di), &buf);
if (error != 0)
return (error);
di = buf;
m->lcn = lcn;
m->nextpackoff = pos + sizeof(*di);
advise = le16toh(di->di_advise);
m->type = advise & Z_EROFS_LI_LCLUSTER_TYPE_MASK;
if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
m->clusterofs = 1U << en->z_lclusterbits;
m->delta[0] = le16toh(di->di_u.delta[0]);
if ((m->delta[0] & Z_EROFS_LI_D0_CBLKCNT) != 0) {
if ((en->z_advise & (Z_EROFS_ADVISE_BIG_PCLUSTER_1 |
Z_EROFS_ADVISE_BIG_PCLUSTER_2)) == 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
m->compressedblks =
m->delta[0] & ~Z_EROFS_LI_D0_CBLKCNT;
m->delta[0] = 1;
}
m->delta[1] = le16toh(di->di_u.delta[1]);
} else {
m->partialref = (advise & Z_EROFS_LI_PARTIAL_REF) != 0;
m->clusterofs = le16toh(di->di_clusterofs);
m->pblk = le32toh(di->di_u.blkaddr);
}
erofs_brelse(buf);
return (0);
}
static unsigned int
decode_compactedbits(unsigned int lobits, const uint8_t *in,
unsigned int pos, uint8_t *type)
{
uint32_t value;
unsigned int lo;
value = le32dec(in + pos / 8) >> (pos & 7);
lo = value & ((1U << lobits) - 1);
*type = (value >> lobits) & 3;
return (lo);
}
static int
get_compacted_la_distance(unsigned int lobits, unsigned int encodebits,
unsigned int vcnt, const uint8_t *in, int i)
{
unsigned int lo, distance;
uint8_t type;
distance = 0;
do {
lo = decode_compactedbits(lobits, in, encodebits * i, &type);
if (type != Z_EROFS_LCLUSTER_TYPE_NONHEAD)
return (distance);
++distance;
} while (++i < (int)vcnt);
if ((lo & Z_EROFS_LI_D0_CBLKCNT) == 0) {
if (lo == 0)
return (-1);
distance += lo - 1;
}
return ((int)distance);
}
static int
z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m, uint64_t lcn,
bool lookahead)
{
struct erofs_node *en;
uint64_t ebase, pos, totalidx, original_lcn;
unsigned int compacted_4b_initial, compacted_2b, amortizedshift;
unsigned int vcnt, lo, lobits, encodebits, nblk, bytes, packsize;
bool big_pcluster;
uint8_t *in, type;
void *buf;
int distance, error, i;
en = m->en;
ebase = Z_EROFS_MAP_HEADER_END(en->inode_off + en->inode_isize +
en->xattr_isize);
totalidx = roundup2(en->size, 1ULL << en->z_lclusterbits) >>
en->z_lclusterbits;
if (lcn >= totalidx || en->z_lclusterbits > 14)
return (EINVAL);
original_lcn = lcn;
m->lcn = lcn;
compacted_4b_initial = ((32 - ebase % 32) / 4) & 7;
compacted_2b = 0;
if ((en->z_advise & Z_EROFS_ADVISE_COMPACTED_2B) != 0 &&
compacted_4b_initial < totalidx)
compacted_2b = rounddown2(totalidx - compacted_4b_initial, 16);
pos = ebase;
amortizedshift = 2;
if (lcn >= compacted_4b_initial) {
pos += compacted_4b_initial * 4;
lcn -= compacted_4b_initial;
if (lcn < compacted_2b) {
amortizedshift = 1;
} else {
pos += compacted_2b * 2;
lcn -= compacted_2b;
}
}
pos += lcn << amortizedshift;
if (amortizedshift == 2 && en->z_lclusterbits <= 14)
vcnt = 2;
else if (amortizedshift == 1 && en->z_lclusterbits <= 12)
vcnt = 16;
else
return (EOPNOTSUPP);
packsize = vcnt << amortizedshift;
bytes = pos & (packsize - 1);
pos -= bytes;
error = z_erofs_read_index(m, pos, packsize, &buf);
if (error != 0)
return (error);
in = buf;
m->nextpackoff = pos + packsize;
lobits = MAX(en->z_lclusterbits, fls(Z_EROFS_LI_D0_CBLKCNT));
encodebits = (packsize - sizeof(uint32_t)) * 8 / vcnt;
i = bytes >> amortizedshift;
lo = decode_compactedbits(lobits, in, encodebits * i, &type);
m->type = type;
if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
m->clusterofs = 1U << en->z_lclusterbits;
if (lookahead) {
distance = get_compacted_la_distance(lobits, encodebits,
vcnt, in, i);
if (distance < 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
m->delta[1] = distance;
}
big_pcluster =
(en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) != 0;
if ((lo & Z_EROFS_LI_D0_CBLKCNT) != 0) {
if (!big_pcluster) {
erofs_brelse(buf);
return (EINTEGRITY);
}
m->compressedblks = lo & ~Z_EROFS_LI_D0_CBLKCNT;
m->delta[0] = 1;
} else if (i + 1 != (int)vcnt) {
m->delta[0] = lo;
} else {
if (i == 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
lo = decode_compactedbits(lobits, in,
encodebits * (i - 1), &type);
if (type != Z_EROFS_LCLUSTER_TYPE_NONHEAD)
lo = 0;
else if ((lo & Z_EROFS_LI_D0_CBLKCNT) != 0)
lo = 1;
m->delta[0] = lo + 1;
}
erofs_brelse(buf);
return (m->delta[0] == 0 ? EINTEGRITY : 0);
}
m->clusterofs = lo;
m->delta[0] = 0;
big_pcluster =
(en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) != 0;
if (!big_pcluster) {
nblk = 1;
while (i > 0) {
--i;
lo = decode_compactedbits(lobits, in,
encodebits * i, &type);
if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD)
i -= lo;
if (i >= 0)
++nblk;
}
} else {
nblk = 0;
while (i > 0) {
--i;
lo = decode_compactedbits(lobits, in,
encodebits * i, &type);
if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
if ((lo & Z_EROFS_LI_D0_CBLKCNT) != 0) {
if (i == 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
--i;
nblk += lo & ~Z_EROFS_LI_D0_CBLKCNT;
continue;
}
if (lo <= 1) {
erofs_brelse(buf);
return (EINTEGRITY);
}
i -= lo - 2;
continue;
}
++nblk;
}
}
m->pblk = le32dec(in + packsize - sizeof(uint32_t)) + nblk;
erofs_brelse(buf);
m->lcn = original_lcn;
return (0);
}
static int
z_erofs_load_lcluster_from_disk(struct z_erofs_maprecorder *m, uint64_t lcn,
bool lookahead)
{
int error;
if (m->en->datalayout == EROFS_INODE_COMPRESSED_COMPACT)
error = z_erofs_load_compact_lcluster(m, lcn, lookahead);
else if (m->en->datalayout == EROFS_INODE_COMPRESSED_FULL)
error = z_erofs_load_full_lcluster(m, lcn);
else
return (EINTEGRITY);
if (error != 0)
return (error);
if (m->type >= Z_EROFS_LCLUSTER_TYPE_MAX)
return (EOPNOTSUPP);
if (m->type != Z_EROFS_LCLUSTER_TYPE_NONHEAD &&
m->clusterofs >= (1U << m->en->z_lclusterbits))
return (EINTEGRITY);
return (0);
}
static int
z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
unsigned int lookback_distance)
{
uint64_t lcn;
int error;
while (lookback_distance != 0 && m->lcn >= lookback_distance) {
lcn = m->lcn - lookback_distance;
error = z_erofs_load_lcluster_from_disk(m, lcn, false);
if (error != 0)
return (error);
if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
lookback_distance = m->delta[0];
continue;
}
m->headtype = m->type;
m->map->m_la = (lcn << m->en->z_lclusterbits) |
m->clusterofs;
return (0);
}
return (EINTEGRITY);
}
static int
z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m,
uint64_t initial_lcn)
{
struct erofs_node *en;
bool bigpcl1, bigpcl2;
uint64_t lcn;
int error;
en = m->en;
bigpcl1 = (en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) != 0;
bigpcl2 = (en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2) != 0;
lcn = m->lcn + 1;
if ((m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD1 && !bigpcl1) ||
((m->headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN ||
m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2) && !bigpcl2) ||
(lcn << en->z_lclusterbits) >= en->size)
m->compressedblks = 1;
if (m->compressedblks == 0) {
error = z_erofs_load_lcluster_from_disk(m, lcn, false);
if (error != 0)
return (error);
if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD &&
m->delta[0] != 1)
return (EINTEGRITY);
if (m->type != Z_EROFS_LCLUSTER_TYPE_NONHEAD ||
m->compressedblks == 0)
m->compressedblks = 1;
}
if (m->compressedblks > (UINT64_MAX >> m->em->block_bits))
return (EOVERFLOW);
m->map->m_plen = m->compressedblks << m->em->block_bits;
(void)initial_lcn;
return (0);
}
static int
z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
{
struct erofs_node *en;
struct erofs_map_blocks *map;
uint64_t lcn, headlcn;
int error;
en = m->en;
map = m->map;
lcn = m->lcn;
headlcn = map->m_la >> en->z_lclusterbits;
for (;;) {
if ((lcn << en->z_lclusterbits) >= en->size) {
map->m_llen = en->size - map->m_la;
return (0);
}
error = z_erofs_load_lcluster_from_disk(m, lcn, true);
if (error != 0)
return (error);
if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
if (m->delta[1] == 0)
m->delta[1] = 1;
} else {
if (lcn != headlcn)
break;
m->delta[1] = 1;
}
if (lcn > UINT64_MAX - m->delta[1])
return (EOVERFLOW);
lcn += m->delta[1];
}
map->m_llen = (lcn << en->z_lclusterbits) + m->clusterofs -
map->m_la;
return (0);
}
static int
z_erofs_map_blocks_fo(struct erofs_mount *em, struct erofs_node *en,
struct erofs_map_blocks *map, int flags)
{
bool fragment, ztailpacking;
struct z_erofs_maprecorder m;
uint64_t initial_lcn, ofs, end;
unsigned int endoff;
int error;
fragment = (en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0;
ztailpacking = en->z_idata_size != 0;
bzero(&m, sizeof(m));
m.em = em;
m.en = en;
m.map = map;
if (en->size == 0) {
map->m_la = 0;
map->m_llen = 0;
map->m_flags = 0;
return (0);
}
ofs = (flags & EROFS_GET_BLOCKS_FINDTAIL) != 0 ?
en->size - 1 : map->m_la;
if (fragment && (flags & EROFS_GET_BLOCKS_FINDTAIL) == 0 &&
en->z_tailextent_headlcn == 0) {
map->m_la = 0;
map->m_llen = en->size;
map->m_flags = EROFS_MAP_FRAGMENT;
return (0);
}
initial_lcn = ofs >> en->z_lclusterbits;
endoff = ofs & ((1U << en->z_lclusterbits) - 1);
error = z_erofs_load_lcluster_from_disk(&m, initial_lcn, false);
if (error != 0)
return (error);
if ((flags & EROFS_GET_BLOCKS_FINDTAIL) != 0 && ztailpacking)
en->z_fragmentoff = m.nextpackoff;
map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_PARTIAL_MAPPED;
end = (m.lcn + 1) << en->z_lclusterbits;
if (m.type != Z_EROFS_LCLUSTER_TYPE_NONHEAD &&
endoff >= m.clusterofs) {
m.headtype = m.type;
map->m_la = (m.lcn << en->z_lclusterbits) | m.clusterofs;
if (ztailpacking && end > en->size)
end = en->size;
} else {
if (m.type != Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
end = (m.lcn << en->z_lclusterbits) | m.clusterofs;
map->m_flags &= ~EROFS_MAP_PARTIAL_MAPPED;
m.delta[0] = 1;
}
error = z_erofs_extent_lookback(&m, m.delta[0]);
if (error != 0)
return (error);
}
if (m.partialref)
map->m_flags |= EROFS_MAP_PARTIAL_REF;
if (end < map->m_la)
return (EINTEGRITY);
map->m_llen = end - map->m_la;
if ((flags & EROFS_GET_BLOCKS_FINDTAIL) != 0) {
en->z_tailextent_headlcn = m.lcn;
if (fragment &&
en->datalayout == EROFS_INODE_COMPRESSED_FULL)
en->z_fragmentoff |= m.pblk << 32;
}
if (ztailpacking && m.lcn == en->z_tailextent_headlcn) {
map->m_flags |= EROFS_MAP_META;
map->m_pa = en->z_fragmentoff;
map->m_plen = en->z_idata_size;
if ((map->m_pa & (em->block_size - 1)) + map->m_plen >
em->block_size)
return (EINTEGRITY);
} else if (fragment && m.lcn == en->z_tailextent_headlcn) {
map->m_flags = EROFS_MAP_FRAGMENT;
} else {
if (m.pblk > (UINT64_MAX >> em->block_bits))
return (EOVERFLOW);
map->m_pa = m.pblk << em->block_bits;
error = z_erofs_get_extent_compressedlen(&m, initial_lcn);
if (error != 0)
return (error);
}
if (m.headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN) {
map->m_algorithmformat =
(en->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER) != 0 ?
Z_EROFS_COMPRESSION_INTERLACED :
Z_EROFS_COMPRESSION_SHIFTED;
} else if (m.headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2) {
map->m_algorithmformat = en->z_algorithmtype[1];
} else {
map->m_algorithmformat = en->z_algorithmtype[0];
}
if ((flags & EROFS_GET_BLOCKS_FIEMAP) != 0 ||
((flags & EROFS_GET_BLOCKS_READMORE) != 0 &&
(map->m_algorithmformat == Z_EROFS_COMPRESSION_LZMA ||
map->m_algorithmformat == Z_EROFS_COMPRESSION_DEFLATE ||
map->m_algorithmformat == Z_EROFS_COMPRESSION_ZSTD) &&
map->m_llen >= em->block_size)) {
error = z_erofs_get_extent_decompressedlen(&m);
if (error == 0)
map->m_flags &= ~EROFS_MAP_PARTIAL_MAPPED;
return (error);
}
return (0);
}
static int
z_erofs_read_extent(struct erofs_mount *em, struct erofs_node *en,
uint64_t pos, unsigned int recsz, struct z_erofs_extent *ext)
{
void *buf;
int error;
bzero(ext, sizeof(*ext));
error = erofs_read_metadata(em, en->nid, pos, recsz, &buf);
if (error != 0)
return (error);
memcpy(ext, buf, recsz);
erofs_brelse(buf);
return (0);
}
static int
z_erofs_extent_add(uint64_t left, uint64_t right, uint64_t *result)
{
if (__builtin_add_overflow(left, right, result))
return (EINTEGRITY);
return (0);
}
static int
z_erofs_extent_roundup(uint64_t value, unsigned int alignment,
uint64_t *result)
{
uint64_t rounded;
if (z_erofs_extent_add(value, alignment - 1, &rounded) != 0)
return (EINTEGRITY);
*result = rounddown2(rounded, alignment);
return (0);
}
static int
z_erofs_extent_table_pos(const struct erofs_node *en, unsigned int recsz,
uint64_t *result)
{
uint64_t pos;
if (z_erofs_extent_add(en->inode_off, en->inode_isize, &pos) != 0 ||
z_erofs_extent_add(pos, en->xattr_isize, &pos) != 0 ||
z_erofs_extent_roundup(pos, 8, &pos) != 0 ||
z_erofs_extent_add(pos, sizeof(struct z_erofs_map_header), &pos) != 0 ||
z_erofs_extent_roundup(pos, recsz, result) != 0)
return (EINTEGRITY);
return (0);
}
static int
z_erofs_extent_record_pos(const struct erofs_node *en, uint64_t table_pos,
unsigned int recsz, uint64_t index, uint64_t *result)
{
uint64_t offset;
if (index >= en->z_extents ||
__builtin_mul_overflow(index, recsz, &offset) ||
z_erofs_extent_add(table_pos, offset, result) != 0)
return (EINTEGRITY);
return (0);
}
static uint64_t
z_erofs_extent_lstart(const struct z_erofs_extent *ext, unsigned int recsz)
{
uint64_t lstart;
lstart = le32toh(ext->lstart_lo);
if (recsz > offsetof(struct z_erofs_extent, lstart_hi))
lstart |= (uint64_t)le32toh(ext->lstart_hi) << 32;
return (lstart);
}
static int
z_erofs_validate_extent_table(struct erofs_mount *em, struct erofs_node *en,
unsigned int recsz)
{
struct z_erofs_extent ext;
uint64_t extent_pos, index, last_pos, lstart, previous;
int error;
if (en->z_extents == 0)
return (en->size == 0 ? 0 : EINTEGRITY);
error = z_erofs_extent_table_pos(en, recsz, &extent_pos);
if (error != 0)
return (error);
if (recsz <= offsetof(struct z_erofs_extent, pstart_lo) &&
z_erofs_extent_add(extent_pos, sizeof(uint64_t), &extent_pos) != 0)
return (EINTEGRITY);
error = z_erofs_extent_record_pos(en, extent_pos, recsz,
en->z_extents - 1, &last_pos);
if (error != 0 || z_erofs_extent_add(last_pos, recsz, &last_pos) != 0)
return (EINTEGRITY);
if (recsz <= offsetof(struct z_erofs_extent, pstart_hi))
return (0);
if (en->size == 0)
return (EINTEGRITY);
previous = 0;
for (index = 0; index < en->z_extents; index++) {
error = z_erofs_extent_record_pos(en, extent_pos, recsz, index,
&last_pos);
if (error != 0)
return (error);
error = z_erofs_read_extent(em, en, last_pos, recsz, &ext);
if (error != 0)
return (error);
lstart = z_erofs_extent_lstart(&ext, recsz);
if (lstart >= en->size || (index != 0 && lstart <= previous))
return (EINTEGRITY);
previous = lstart;
}
return (0);
}
static int
z_erofs_map_blocks_ext(struct erofs_mount *em, struct erofs_node *en,
struct erofs_map_blocks *map, int flags)
{
struct z_erofs_extent ext;
unsigned int recsz, bmask, fmt;
uint64_t cluster_size, extent_idx, extent_pos, next, pos, rounded_lend;
uint64_t lend, l, r, mid, pa, la, lstart, table_pos;
bool interlaced, last;
void *buf;
int error;
(void)flags;
interlaced =
(en->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER) != 0;
recsz = z_erofs_extent_recsize(en->z_advise);
error = z_erofs_extent_table_pos(en, recsz, &table_pos);
if (error != 0)
return (error);
pos = table_pos;
bmask = em->block_size - 1;
lend = en->size;
cluster_size = 1ULL << en->z_lclusterbits;
map->m_flags = 0;
if (recsz <= offsetof(struct z_erofs_extent, pstart_hi)) {
if (recsz <= offsetof(struct z_erofs_extent, pstart_lo)) {
error = erofs_read_metadata(em, en->nid, pos,
sizeof(uint64_t), &buf);
if (error != 0)
return (error);
pa = le64dec(buf);
erofs_brelse(buf);
if (z_erofs_extent_add(pos, sizeof(uint64_t), &pos) != 0)
return (EINTEGRITY);
lstart = 0;
extent_idx = 0;
} else {
lstart = rounddown2(map->m_la, cluster_size);
extent_idx = lstart >> en->z_lclusterbits;
pa = EROFS_NULL_ADDR;
}
for (;;) {
error = z_erofs_extent_record_pos(en, pos, recsz,
extent_idx, &extent_pos);
if (error != 0)
return (error);
error = z_erofs_read_extent(em, en, extent_pos, recsz, &ext);
if (error != 0)
return (error);
map->m_plen = le32toh(ext.plen);
if (pa != EROFS_NULL_ADDR) {
map->m_pa = pa;
if (z_erofs_extent_add(pa,
map->m_plen & Z_EROFS_EXTENT_PLEN_MASK,
&next) != 0)
return (EINTEGRITY);
pa = next;
} else {
map->m_pa = le32toh(ext.pstart_lo);
}
if (extent_idx == UINT64_MAX)
return (EINTEGRITY);
extent_idx++;
if (z_erofs_extent_add(lstart, cluster_size, &next) != 0)
return (EINTEGRITY);
lstart = next;
if (lstart > map->m_la)
break;
}
if (z_erofs_extent_roundup(lend, cluster_size, &rounded_lend) != 0)
return (EINTEGRITY);
last = lstart >= rounded_lend;
lend = MIN(lstart, lend);
lstart -= cluster_size;
} else {
lstart = lend;
for (l = 0, r = en->z_extents; l < r;) {
mid = l + (r - l) / 2;
error = z_erofs_extent_record_pos(en, table_pos, recsz, mid,
&extent_pos);
if (error != 0)
return (error);
error = z_erofs_read_extent(em, en, extent_pos,
recsz, &ext);
if (error != 0)
return (error);
la = z_erofs_extent_lstart(&ext, recsz);
pa = le32toh(ext.pstart_lo) |
((uint64_t)le32toh(ext.pstart_hi) << 32);
if (la > map->m_la) {
r = mid;
if (la > lend)
return (EINTEGRITY);
lend = la;
} else {
l = mid + 1;
if (map->m_la == la)
r = MIN(l + 1, r);
lstart = la;
map->m_plen = le32toh(ext.plen);
map->m_pa = pa;
}
}
last = l >= en->z_extents;
}
if (lstart < lend) {
map->m_la = lstart;
if (last &&
(en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0) {
map->m_flags = EROFS_MAP_FRAGMENT;
en->z_fragmentoff = map->m_plen;
if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
en->z_fragmentoff |= map->m_pa << 32;
} else if ((map->m_plen & Z_EROFS_EXTENT_PLEN_MASK) != 0) {
map->m_flags = EROFS_MAP_MAPPED;
fmt = map->m_plen >> Z_EROFS_EXTENT_PLEN_FMT_BIT;
if ((map->m_plen & Z_EROFS_EXTENT_PLEN_PARTIAL) != 0)
map->m_flags |= EROFS_MAP_PARTIAL_REF;
map->m_plen &= Z_EROFS_EXTENT_PLEN_MASK;
if (fmt != 0)
map->m_algorithmformat = fmt - 1;
else if (interlaced &&
((map->m_pa | map->m_plen) & bmask) == 0)
map->m_algorithmformat =
Z_EROFS_COMPRESSION_INTERLACED;
else
map->m_algorithmformat =
Z_EROFS_COMPRESSION_SHIFTED;
}
}
map->m_llen = lend - map->m_la;
return (0);
}
int
z_erofs_fill_inode(struct erofs_mount *em, struct erofs_node *en)
{
struct z_erofs_map_header *h;
struct erofs_map_blocks map;
uint64_t cluster_size, raw, pos, rounded_size;
unsigned int recsz;
void *buf;
int error;
if (en->z_initialized)
return (0);
if (z_erofs_extent_add(en->inode_off, en->inode_isize, &pos) != 0 ||
z_erofs_extent_add(pos, en->xattr_isize, &pos) != 0 ||
z_erofs_extent_roundup(pos, 8, &pos) != 0)
return (EINTEGRITY);
error = erofs_read_metadata(em, en->nid, pos, sizeof(*h), &buf);
if (error != 0)
return (error);
h = buf;
if ((h->h_clusterbits & (1U << Z_EROFS_FRAGMENT_INODE_BIT)) != 0) {
if (!erofs_sb_has_fragments(em) || em->packed_nid == 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
raw = le64dec(h);
en->z_advise = Z_EROFS_ADVISE_FRAGMENT_PCLUSTER;
en->z_fragmentoff = raw ^ (1ULL << 63);
en->z_tailextent_headlcn = 0;
en->fragment = true;
erofs_brelse(buf);
en->z_initialized = true;
return (0);
}
en->z_advise = le16toh(h->h_advise);
en->z_lclusterbits = em->block_bits + (h->h_clusterbits & 15);
if (en->z_lclusterbits >= 31) {
erofs_brelse(buf);
return (EINTEGRITY);
}
if (en->datalayout == EROFS_INODE_COMPRESSED_FULL &&
(en->z_advise & Z_EROFS_ADVISE_EXTENTS) != 0) {
recsz = z_erofs_extent_recsize(en->z_advise);
if (recsz <= offsetof(struct z_erofs_extent, pstart_hi)) {
cluster_size = 1ULL << en->z_lclusterbits;
if (z_erofs_extent_roundup(en->size, cluster_size,
&rounded_size) != 0) {
erofs_brelse(buf);
return (EINTEGRITY);
}
en->z_extents = rounded_size >> en->z_lclusterbits;
} else {
en->z_extents = le32toh(h->h_extents_lo) |
((uint64_t)le16toh(h->h_extents_hi) << 32);
}
en->fragment =
(en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0;
erofs_brelse(buf);
if (en->fragment &&
(!erofs_sb_has_fragments(em) || em->packed_nid == 0))
return (EINTEGRITY);
if (recsz > offsetof(struct z_erofs_extent, pstart_hi) &&
en->z_extents == 0 && en->size != 0)
return (EINTEGRITY);
error = z_erofs_validate_extent_table(em, en, recsz);
if (error != 0)
return (error);
en->z_initialized = true;
return (0);
}
en->z_algorithmtype[0] = h->h_algorithmtype & 15;
en->z_algorithmtype[1] = h->h_algorithmtype >> 4;
if ((en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0)
en->z_fragmentoff = le32toh(h->h_fragmentoff);
else if ((en->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) != 0)
en->z_idata_size = le16toh(h->h_idata_size);
erofs_brelse(buf);
if (!erofs_sb_has_big_pcluster(em) &&
(en->z_advise & (Z_EROFS_ADVISE_BIG_PCLUSTER_1 |
Z_EROFS_ADVISE_BIG_PCLUSTER_2)) != 0)
return (EINTEGRITY);
if (en->datalayout == EROFS_INODE_COMPRESSED_COMPACT &&
(((en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) != 0) !=
((en->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2) != 0)))
return (EINTEGRITY);
if (en->z_idata_size != 0 ||
(en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0) {
if ((en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0 &&
(!erofs_sb_has_fragments(em) || em->packed_nid == 0))
return (EINTEGRITY);
bzero(&map, sizeof(map));
error = z_erofs_map_blocks_fo(em, en, &map,
EROFS_GET_BLOCKS_FINDTAIL);
if (error != 0)
return (error);
}
en->fragment =
(en->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) != 0;
en->z_initialized = true;
return (0);
}
static int
z_erofs_map_sanity_check(struct erofs_mount *em, struct erofs_node *en,
struct erofs_map_blocks *map)
{
uint64_t pend;
if ((map->m_flags & EROFS_MAP_FRAGMENT) != 0) {
if ((map->m_flags & (EROFS_MAP_MAPPED | EROFS_MAP_META)) != 0 ||
em->packed_inode == NULL || em->packed_inode->nid == en->nid ||
en->z_fragmentoff > em->packed_inode->size ||
map->m_llen > em->packed_inode->size - en->z_fragmentoff)
return (EINTEGRITY);
return (0);
}
if ((map->m_flags & EROFS_MAP_MAPPED) == 0)
return (0);
if ((unsigned char)map->m_algorithmformat >=
Z_EROFS_COMPRESSION_RUNTIME_MAX)
return (EOPNOTSUPP);
if (map->m_algorithmformat < Z_EROFS_COMPRESSION_MAX) {
if ((em->available_compr_algs &
(1U << map->m_algorithmformat)) == 0)
return (EINTEGRITY);
if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen)
return (EINTEGRITY);
} else if (map->m_llen > map->m_plen) {
return (EINTEGRITY);
}
if (map->m_plen > Z_EROFS_PCLUSTER_MAX_SIZE ||
map->m_llen > Z_EROFS_PCLUSTER_MAX_DSIZE)
return (EOPNOTSUPP);
if ((map->m_flags & EROFS_MAP_META) != 0)
return (0);
if (__builtin_add_overflow(map->m_pa, map->m_plen, &pend))
return (EINTEGRITY);
if ((pend >> em->block_bits) >= (1ULL << 48))
return (EINTEGRITY);
(void)en;
return (0);
}
int
z_erofs_map_blocks_iter(struct erofs_mount *em, struct erofs_node *en,
struct erofs_map_blocks *map, int flags)
{
int error;
if (map->m_la >= en->size) {
map->m_llen = map->m_la + 1 - en->size;
map->m_la = en->size;
map->m_flags = 0;
return (0);
}
error = z_erofs_fill_inode(em, en);
if (error == 0) {
if (en->datalayout == EROFS_INODE_COMPRESSED_FULL &&
(en->z_advise & Z_EROFS_ADVISE_EXTENTS) != 0)
error = z_erofs_map_blocks_ext(em, en, map, flags);
else
error = z_erofs_map_blocks_fo(em, en, map, flags);
}
if (error == 0)
error = z_erofs_map_sanity_check(em, en, map);
if (error != 0)
map->m_llen = 0;
return (error);
}