test code v1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user