diff --git a/Makefile.util.def b/Makefile.util.def index 3ee5e4e..3ab043a 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -96,6 +96,7 @@ library = { common = grub-core/fs/zfs/zfs.c; common = grub-core/fs/zfs/zfsinfo.c; common = grub-core/fs/zfs/zfs_lzjb.c; + common = grub-core/fs/zfs/zfs_lz4.c; common = grub-core/fs/zfs/zfs_sha256.c; common = grub-core/fs/zfs/zfs_fletcher.c; common = grub-core/lib/envblk.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 4609a4b..94dc6c9 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1224,6 +1224,7 @@ module = { name = zfs; common = fs/zfs/zfs.c; common = fs/zfs/zfs_lzjb.c; + common = fs/zfs/zfs_lz4.c; common = fs/zfs/zfs_sha256.c; common = fs/zfs/zfs_fletcher.c; }; diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index cf10534..aaf1af4 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -154,11 +154,13 @@ ZAP_LEAF_ENTRY(zap_leaf_phys_t *l, int bs, int idx) /* - * Decompression Entry - lzjb + * Decompression Entry - lzjb & lz4 */ extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t); +extern grub_err_t lz4_decompress (void *, void *, grub_size_t, grub_size_t); + typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start, grub_size_t s_len, grub_size_t d_len); typedef struct decomp_entry @@ -278,7 +280,7 @@ grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key * to be listed here since grub opens pools in read-only mode. */ static const char *spa_feature_names[] = { - "max.test:feat1",NULL + "org.illumos:lz4_compress",NULL }; static int @@ -344,6 +346,7 @@ static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ {"zle", zle_decompress}, /* ZIO_COMPRESS_ZLE */ + {"lz4", lz4_decompress}, /* ZIO_COMPRESS_LZ4 */ }; static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, diff --git a/grub-core/fs/zfs/zfs_lz4.c b/grub-core/fs/zfs/zfs_lz4.c new file mode 100644 index 0000000..f199434 --- /dev/null +++ b/grub-core/fs/zfs/zfs_lz4.c @@ -0,0 +1,321 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2013, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + */ + +#include +#include +#include +#include + +static int LZ4_uncompress_unknownOutputSize(const char *source, char *dest, + int isize, int maxOutputSize); + +/* + * CPU Feature Detection + */ + +/* 32 or 64 bits ? */ +#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || \ + defined(__amd64) || defined(__ppc64__) || defined(_WIN64) || \ + defined(__LP64__) || defined(_LP64)) +#define LZ4_ARCH64 1 +#else +#define LZ4_ARCH64 0 +#endif + +/* + * Little Endian or Big Endian? + * Note: overwrite the below #define if you know your architecture endianess. + */ +#if (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || \ + defined(_BIG_ENDIAN) || defined(_ARCH_PPC) || defined(__PPC__) || \ + defined(__PPC) || defined(PPC) || defined(__powerpc__) || \ + defined(__powerpc) || defined(powerpc) || \ + ((defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))) +#define LZ4_BIG_ENDIAN 1 +#else + /* + * Little Endian assumed. PDP Endian and other very rare endian format + * are unsupported. + */ +#endif + +/* + * Compiler Options + */ + +#if __STDC_VERSION__ >= 199901L /* C99 */ +/* "restrict" is a known keyword */ +#else +/* Disable restrict */ +#ifndef restrict +#define restrict /* Only if somebody already didn't take care of that.*/ +#endif +#endif + +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) \ + | (((x) & 0xffu) << 8))) + +#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +#define expect(expr, value) (__builtin_expect((expr), (value))) +#else +#define expect(expr, value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + +/* Basic types */ +#define BYTE grub_uint8_t +#define U16 grub_uint16_t +#define U32 grub_uint32_t +#define S32 grub_int32_t +#define U64 grub_uint64_t +typedef grub_size_t size_t; + +typedef struct _U16_S { + U16 v; +} U16_S; +typedef struct _U32_S { + U32 v; +} U32_S; +typedef struct _U64_S { + U64 v; +} U64_S; + +#define A64(x) (((U64_S *)(x))->v) +#define A32(x) (((U32_S *)(x))->v) +#define A16(x) (((U16_S *)(x))->v) + +/* + * Constants + */ +#define MINMATCH 4 + +#define COPYLENGTH 8 +#define LASTLITERALS 5 + +#define ML_BITS 4 +#define ML_MASK ((1U< s_len) + return grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed."); + + /* + * Returns 0 on success (decompression function returned non-negative) + * and appropriate error on failure (decompression function returned negative). + */ + return (LZ4_uncompress_unknownOutputSize((char*)s_start + 4, d_start, bufsiz, + d_len) < 0)?grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed."):0; +} + +static int +LZ4_uncompress_unknownOutputSize(const char *source, + char *dest, int isize, int maxOutputSize) +{ + /* Local Variables */ + const BYTE *restrict ip = (const BYTE *) source; + const BYTE *const iend = ip + isize; + const BYTE *restrict ref; + + BYTE *restrict op = (BYTE *) dest; + BYTE *const oend = op + maxOutputSize; + BYTE *cpy; + + size_t dec[] = { 0, 3, 2, 3, 0, 0, 0, 0 }; + + /* Main Loop */ + while (ip < iend) { + BYTE token; + int length; + + /* get runlength */ + token = *ip++; + if ((length = (token >> ML_BITS)) == RUN_MASK) { + int s = 255; + while ((ip < iend) && (s == 255)) { + s = *ip++; + length += s; + } + } + /* copy literals */ + cpy = op + length; + if ((cpy > oend - COPYLENGTH) || + (ip + length > iend - COPYLENGTH)) { + if (cpy > oend) + /* + * Error: request to write beyond destination + * buffer. + */ + goto _output_error; + if (ip + length > iend) + /* + * Error : request to read beyond source + * buffer. + */ + goto _output_error; + grub_memcpy(op, ip, length); + op += length; + ip += length; + if (ip < iend) + /* Error : LZ4 format violation */ + goto _output_error; + /* Necessarily EOF, due to parsing restrictions. */ + break; + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + if (ref < (BYTE * const) dest) + /* + * Error: offset creates reference outside of + * destination buffer. + */ + goto _output_error; + + /* get matchlength */ + if ((length = (token & ML_MASK)) == ML_MASK) { + while (ip < iend) { + int s = *ip++; + length += s; + if (s == 255) + continue; + break; + } + } + /* copy repeated sequence */ + if unlikely(op - ref < STEPSIZE) { +#if LZ4_ARCH64 + size_t dec2table[] = { 0, 0, 0, -1, 0, 1, 2, 3 }; + size_t dec2 = dec2table[op - ref]; +#else + const int dec2 = 0; +#endif + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + ref -= dec[op - ref]; + A32(op) = A32(ref); + op += STEPSIZE - 4; + ref -= dec2; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE - 4); + if (cpy > oend - COPYLENGTH) { + if (cpy > oend) + /* + * Error: request to write outside of + * destination buffer. + */ + goto _output_error; + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + if (op == oend) + /* + * Check EOF (should never happen, since last + * 5 bytes are supposed to be literals). + */ + break; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + + /* end of decoding */ + return (int)(((char *)op) - dest); + + /* write overflow error detected */ + _output_error: + return (int)(-(((char *)ip) - source)); +} diff --git a/include/grub/zfs/zio.h b/include/grub/zfs/zio.h index b1c46da..8fad2cc 100644 --- a/include/grub/zfs/zio.h +++ b/include/grub/zfs/zio.h @@ -88,6 +88,7 @@ enum zio_compress { ZIO_COMPRESS_GZIP8, ZIO_COMPRESS_GZIP9, ZIO_COMPRESS_ZLE, + ZIO_COMPRESS_LZ4, ZIO_COMPRESS_FUNCTIONS }; diff --git a/po/POTFILES.in b/po/POTFILES.in index 01cc53c..a0a5d60 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -174,6 +174,7 @@ ./grub-core/fs/zfs/zfs_fletcher.c ./grub-core/fs/zfs/zfsinfo.c ./grub-core/fs/zfs/zfs_lzjb.c +./grub-core/fs/zfs/zfs_lz4.c ./grub-core/fs/zfs/zfs_sha256.c ./grub-core/gdb/cstub.c ./grub-core/gdb/gdb.c