/* SPDX-License-Identifier: BSD-2-Clause */ /* Minimal LZ4 decompressor for EROFS FreeBSD */ #include #include #include "erofs_defs.h" #include "internal.h" static int lz4_finish(const uint8_t *ip, const uint8_t *iend, int partial) { if (partial) return (0); while (ip < iend) { if (*ip++ != 0) return (-1); } return (0); } int lz4_decompress(void *src, void *dst, size_t srclen, size_t dstlen, int partial) { const uint8_t *ip, *iend; uint8_t *op, *oend; unsigned int token; size_t length, copylen; size_t offset; ip = src; iend = ip + srclen; op = dst; oend = op + dstlen; if (iend < ip || oend < op) return (-1); while (ip < iend) { token = *ip++; length = token >> EROFS_LZ4_TOKEN_LITERAL_SHIFT; if (length == EROFS_LZ4_MAX_RUN) { unsigned int value; do { if (ip >= iend) return (-1); value = *ip++; if (length > SIZE_MAX - value) return (-1); length += value; } while (value == EROFS_LZ4_EXT_SENTINEL); } if (length > (size_t)(iend - ip)) return (-1); if (!partial && length > (size_t)(oend - op)) return (-1); copylen = MIN(length, (size_t)(oend - op)); memcpy(op, ip, copylen); ip += length; op += copylen; if (op == oend) return (lz4_finish(ip, iend, partial)); if (ip >= iend) break; if (ip + EROFS_LZ4_OFFSET_BYTES > iend) return (-1); offset = ip[0] | (ip[1] << 8); ip += EROFS_LZ4_OFFSET_BYTES; if (offset == 0 || offset > (size_t)(op - (uint8_t *)dst)) return (-1); length = token & EROFS_LZ4_TOKEN_MATCH_MASK; if (length == EROFS_LZ4_MAX_RUN) { unsigned int value; do { if (ip >= iend) return (-1); value = *ip++; if (length > SIZE_MAX - value) return (-1); length += value; } while (value == EROFS_LZ4_EXT_SENTINEL); } if (length > SIZE_MAX - EROFS_LZ4_MIN_MATCH) return (-1); length += EROFS_LZ4_MIN_MATCH; if (!partial && length > (size_t)(oend - op)) return (-1); copylen = MIN(length, (size_t)(oend - op)); while (copylen-- != 0) { *op = *(op - offset); ++op; } if (op == oend) return (lz4_finish(ip, iend, partial)); } return (op == oend ? lz4_finish(ip, iend, partial) : -1); }