Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Squashfs - a compressed read only filesystem for Linux
0004  *
0005  * Copyright (c) 2016-present, Facebook, Inc.
0006  * All rights reserved.
0007  *
0008  * zstd_wrapper.c
0009  */
0010 
0011 #include <linux/mutex.h>
0012 #include <linux/bio.h>
0013 #include <linux/slab.h>
0014 #include <linux/zstd.h>
0015 #include <linux/vmalloc.h>
0016 
0017 #include "squashfs_fs.h"
0018 #include "squashfs_fs_sb.h"
0019 #include "squashfs.h"
0020 #include "decompressor.h"
0021 #include "page_actor.h"
0022 
0023 struct workspace {
0024     void *mem;
0025     size_t mem_size;
0026     size_t window_size;
0027 };
0028 
0029 static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
0030 {
0031     struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);
0032 
0033     if (wksp == NULL)
0034         goto failed;
0035     wksp->window_size = max_t(size_t,
0036             msblk->block_size, SQUASHFS_METADATA_SIZE);
0037     wksp->mem_size = zstd_dstream_workspace_bound(wksp->window_size);
0038     wksp->mem = vmalloc(wksp->mem_size);
0039     if (wksp->mem == NULL)
0040         goto failed;
0041 
0042     return wksp;
0043 
0044 failed:
0045     ERROR("Failed to allocate zstd workspace\n");
0046     kfree(wksp);
0047     return ERR_PTR(-ENOMEM);
0048 }
0049 
0050 
0051 static void zstd_free(void *strm)
0052 {
0053     struct workspace *wksp = strm;
0054 
0055     if (wksp)
0056         vfree(wksp->mem);
0057     kfree(wksp);
0058 }
0059 
0060 
0061 static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
0062     struct bio *bio, int offset, int length,
0063     struct squashfs_page_actor *output)
0064 {
0065     struct workspace *wksp = strm;
0066     zstd_dstream *stream;
0067     size_t total_out = 0;
0068     int error = 0;
0069     zstd_in_buffer in_buf = { NULL, 0, 0 };
0070     zstd_out_buffer out_buf = { NULL, 0, 0 };
0071     struct bvec_iter_all iter_all = {};
0072     struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
0073 
0074     stream = zstd_init_dstream(wksp->window_size, wksp->mem, wksp->mem_size);
0075 
0076     if (!stream) {
0077         ERROR("Failed to initialize zstd decompressor\n");
0078         return -EIO;
0079     }
0080 
0081     out_buf.size = PAGE_SIZE;
0082     out_buf.dst = squashfs_first_page(output);
0083     if (IS_ERR(out_buf.dst)) {
0084         error = PTR_ERR(out_buf.dst);
0085         goto finish;
0086     }
0087 
0088     for (;;) {
0089         size_t zstd_err;
0090 
0091         if (in_buf.pos == in_buf.size) {
0092             const void *data;
0093             int avail;
0094 
0095             if (!bio_next_segment(bio, &iter_all)) {
0096                 error = -EIO;
0097                 break;
0098             }
0099 
0100             avail = min(length, ((int)bvec->bv_len) - offset);
0101             data = bvec_virt(bvec);
0102             length -= avail;
0103             in_buf.src = data + offset;
0104             in_buf.size = avail;
0105             in_buf.pos = 0;
0106             offset = 0;
0107         }
0108 
0109         if (out_buf.pos == out_buf.size) {
0110             out_buf.dst = squashfs_next_page(output);
0111             if (IS_ERR(out_buf.dst)) {
0112                 error = PTR_ERR(out_buf.dst);
0113                 break;
0114             } else if (out_buf.dst == NULL) {
0115                 /* Shouldn't run out of pages
0116                  * before stream is done.
0117                  */
0118                 error = -EIO;
0119                 break;
0120             }
0121             out_buf.pos = 0;
0122             out_buf.size = PAGE_SIZE;
0123         }
0124 
0125         total_out -= out_buf.pos;
0126         zstd_err = zstd_decompress_stream(stream, &out_buf, &in_buf);
0127         total_out += out_buf.pos; /* add the additional data produced */
0128         if (zstd_err == 0)
0129             break;
0130 
0131         if (zstd_is_error(zstd_err)) {
0132             ERROR("zstd decompression error: %d\n",
0133                     (int)zstd_get_error_code(zstd_err));
0134             error = -EIO;
0135             break;
0136         }
0137     }
0138 
0139 finish:
0140 
0141     squashfs_finish_page(output);
0142 
0143     return error ? error : total_out;
0144 }
0145 
0146 const struct squashfs_decompressor squashfs_zstd_comp_ops = {
0147     .init = zstd_init,
0148     .free = zstd_free,
0149     .decompress = zstd_uncompress,
0150     .id = ZSTD_COMPRESSION,
0151     .name = "zstd",
0152     .alloc_buffer = 1,
0153     .supported = 1
0154 };