uboot/fs/btrfs/compression.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * BTRFS filesystem implementation for U-Boot
   4 *
   5 * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org
   6 */
   7
   8#include "btrfs.h"
   9#include <abuf.h>
  10#include <log.h>
  11#include <malloc.h>
  12#include <linux/lzo.h>
  13#include <linux/zstd.h>
  14#include <linux/compat.h>
  15#include <u-boot/zlib.h>
  16#include <asm/unaligned.h>
  17
  18/* Header for each segment, LE32, recording the compressed size */
  19#define LZO_LEN         4
  20static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
  21{
  22        u32 tot_len, tot_in, in_len, res;
  23        size_t out_len;
  24        int ret;
  25
  26        if (clen < LZO_LEN)
  27                return -1;
  28
  29        tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
  30        tot_in = 0;
  31        cbuf += LZO_LEN;
  32        clen -= LZO_LEN;
  33        tot_len -= LZO_LEN;
  34        tot_in += LZO_LEN;
  35
  36        if (tot_len == 0 && dlen)
  37                return -1;
  38        if (tot_len < LZO_LEN)
  39                return -1;
  40
  41        res = 0;
  42
  43        while (tot_len > LZO_LEN) {
  44                u32 rem_page;
  45
  46                in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
  47                cbuf += LZO_LEN;
  48                clen -= LZO_LEN;
  49
  50                if (in_len > clen || tot_len < LZO_LEN + in_len)
  51                        return -1;
  52
  53                tot_len -= (LZO_LEN + in_len);
  54                tot_in += (LZO_LEN + in_len);
  55
  56                out_len = dlen;
  57                ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
  58                if (ret != LZO_E_OK)
  59                        return -1;
  60
  61                cbuf += in_len;
  62                clen -= in_len;
  63                dbuf += out_len;
  64                dlen -= out_len;
  65
  66                res += out_len;
  67
  68                /*
  69                 * If the 4 bytes header does not fit to the rest of the page we
  70                 * have to move to next one, or we read some garbage.
  71                 */
  72                rem_page = PAGE_SIZE - (tot_in % PAGE_SIZE);
  73                if (rem_page < LZO_LEN) {
  74                        cbuf += rem_page;
  75                        tot_in += rem_page;
  76                        clen -= rem_page;
  77                        tot_len -= rem_page;
  78                }
  79        }
  80
  81        return res;
  82}
  83
  84/* from zutil.h */
  85#define PRESET_DICT 0x20
  86
  87static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
  88{
  89        int wbits = MAX_WBITS, ret = -1;
  90        z_stream stream;
  91        u8 *cbuf;
  92        u32 res;
  93
  94        memset(&stream, 0, sizeof(stream));
  95
  96        cbuf = (u8 *) _cbuf;
  97
  98        stream.total_in = 0;
  99
 100        stream.next_out = dbuf;
 101        stream.avail_out = dlen;
 102        stream.total_out = 0;
 103
 104        /* skip adler32 check if deflate and no dictionary */
 105        if (clen > 2 && !(cbuf[1] & PRESET_DICT) &&
 106            ((cbuf[0] & 0x0f) == Z_DEFLATED) &&
 107            !(((cbuf[0] << 8) + cbuf[1]) % 31)) {
 108                wbits = -((cbuf[0] >> 4) + 8);
 109                cbuf += 2;
 110                clen -= 2;
 111        }
 112
 113        if (Z_OK != inflateInit2(&stream, wbits))
 114                return -1;
 115
 116        while (stream.total_in < clen) {
 117                stream.next_in = cbuf + stream.total_in;
 118                stream.avail_in = min((u32) (clen - stream.total_in),
 119                                        current_fs_info->sectorsize);
 120
 121                ret = inflate(&stream, Z_NO_FLUSH);
 122                if (ret != Z_OK)
 123                        break;
 124        }
 125
 126        res = stream.total_out;
 127        inflateEnd(&stream);
 128
 129        if (ret != Z_STREAM_END)
 130                return -1;
 131
 132        return res;
 133}
 134
 135#define ZSTD_BTRFS_MAX_WINDOWLOG 17
 136#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
 137
 138static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
 139{
 140        struct abuf in, out;
 141
 142        abuf_init_set(&in, (u8 *)cbuf, clen);
 143        abuf_init_set(&out, dbuf, dlen);
 144
 145        return zstd_decompress(&in, &out);
 146}
 147
 148u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
 149{
 150        u32 res;
 151        const u8 *cbuf;
 152        u8 *dbuf;
 153
 154        cbuf = (const u8 *) c;
 155        dbuf = (u8 *) d;
 156
 157        switch (type) {
 158        case BTRFS_COMPRESS_NONE:
 159                res = dlen < clen ? dlen : clen;
 160                memcpy(dbuf, cbuf, res);
 161                return res;
 162        case BTRFS_COMPRESS_ZLIB:
 163                return decompress_zlib(cbuf, clen, dbuf, dlen);
 164        case BTRFS_COMPRESS_LZO:
 165                return decompress_lzo(cbuf, clen, dbuf, dlen);
 166        case BTRFS_COMPRESS_ZSTD:
 167                return decompress_zstd(cbuf, clen, dbuf, dlen);
 168        default:
 169                printf("%s: Unsupported compression in extent: %i\n", __func__,
 170                       type);
 171                return -1;
 172        }
 173}
 174