| 1 | |
| 2 | |
| 3 | * Squashfs - a compressed read only filesystem for Linux
|
| 4 | *
|
| 5 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
| 6 | * Phillip Lougher <[email protected]>
|
| 7 | *
|
| 8 | * xz_wrapper.c
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 | #include <linux/mutex.h>
|
| 13 | #include <linux/bio.h>
|
| 14 | #include <linux/slab.h>
|
| 15 | #include <linux/xz.h>
|
| 16 | #include <linux/bitops.h>
|
| 17 |
|
| 18 | #include "squashfs_fs.h"
|
| 19 | #include "squashfs_fs_sb.h"
|
| 20 | #include "squashfs.h"
|
| 21 | #include "decompressor.h"
|
| 22 | #include "page_actor.h"
|
| 23 |
|
| 24 | struct squashfs_xz {
|
| 25 | struct xz_dec *state;
|
| 26 | struct xz_buf buf;
|
| 27 | };
|
| 28 |
|
| 29 | struct disk_comp_opts {
|
| 30 | __le32 dictionary_size;
|
| 31 | __le32 flags;
|
| 32 | };
|
| 33 |
|
| 34 | struct comp_opts {
|
| 35 | int dict_size;
|
| 36 | };
|
| 37 |
|
| 38 | static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
|
| 39 | void *buff, int len)
|
| 40 | {
|
| 41 | struct disk_comp_opts *comp_opts = buff;
|
| 42 | struct comp_opts *opts;
|
| 43 | int err = 0, n;
|
| 44 |
|
| 45 | opts = kmalloc_obj(*opts);
|
| 46 | if (opts == NULL) {
|
| 47 | err = -ENOMEM;
|
| 48 | goto out2;
|
| 49 | }
|
| 50 |
|
| 51 | if (comp_opts) {
|
| 52 |
|
| 53 | if (len < sizeof(*comp_opts)) {
|
| 54 | err = -EIO;
|
| 55 | goto out;
|
| 56 | }
|
| 57 |
|
| 58 | opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
|
| 59 |
|
| 60 |
|
| 61 | n = ffs(opts->dict_size) - 1;
|
| 62 | if (opts->dict_size <= 0 || (opts->dict_size != (1 << n) &&
|
| 63 | opts->dict_size != (1 << n) + (1 << (n + 1)))) {
|
| 64 | err = -EIO;
|
| 65 | goto out;
|
| 66 | }
|
| 67 | } else
|
| 68 |
|
| 69 | opts->dict_size = max_t(int, msblk->block_size,
|
| 70 | SQUASHFS_METADATA_SIZE);
|
| 71 |
|
| 72 | return opts;
|
| 73 |
|
| 74 | out:
|
| 75 | kfree(opts);
|
| 76 | out2:
|
| 77 | return ERR_PTR(err);
|
| 78 | }
|
| 79 |
|
| 80 |
|
| 81 | static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
|
| 82 | {
|
| 83 | struct comp_opts *comp_opts = buff;
|
| 84 | struct squashfs_xz *stream;
|
| 85 | int err;
|
| 86 |
|
| 87 | stream = kmalloc_obj(*stream);
|
| 88 | if (stream == NULL) {
|
| 89 | err = -ENOMEM;
|
| 90 | goto failed;
|
| 91 | }
|
| 92 |
|
| 93 | stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
|
| 94 | if (stream->state == NULL) {
|
| 95 | kfree(stream);
|
| 96 | err = -ENOMEM;
|
| 97 | goto failed;
|
| 98 | }
|
| 99 |
|
| 100 | return stream;
|
| 101 |
|
| 102 | failed:
|
| 103 | ERROR("Failed to initialise xz decompressor\n");
|
| 104 | return ERR_PTR(err);
|
| 105 | }
|
| 106 |
|
| 107 |
|
| 108 | static void squashfs_xz_free(void *strm)
|
| 109 | {
|
| 110 | struct squashfs_xz *stream = strm;
|
| 111 |
|
| 112 | if (stream) {
|
| 113 | xz_dec_end(stream->state);
|
| 114 | kfree(stream);
|
| 115 | }
|
| 116 | }
|
| 117 |
|
| 118 |
|
| 119 | static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
| 120 | struct bio *bio, int offset, int length,
|
| 121 | struct squashfs_page_actor *output)
|
| 122 | {
|
| 123 | struct bvec_iter_all iter_all = {};
|
| 124 | struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
|
| 125 | int total = 0, error = 0;
|
| 126 | struct squashfs_xz *stream = strm;
|
| 127 |
|
| 128 | xz_dec_reset(stream->state);
|
| 129 | stream->buf.in_pos = 0;
|
| 130 | stream->buf.in_size = 0;
|
| 131 | stream->buf.out_pos = 0;
|
| 132 | stream->buf.out_size = PAGE_SIZE;
|
| 133 | stream->buf.out = squashfs_first_page(output);
|
| 134 | if (IS_ERR(stream->buf.out)) {
|
| 135 | error = PTR_ERR(stream->buf.out);
|
| 136 | goto finish;
|
| 137 | }
|
| 138 |
|
| 139 | for (;;) {
|
| 140 | enum xz_ret xz_err;
|
| 141 |
|
| 142 | if (stream->buf.in_pos == stream->buf.in_size) {
|
| 143 | const void *data;
|
| 144 | int avail;
|
| 145 |
|
| 146 | if (!bio_next_segment(bio, &iter_all)) {
|
| 147 |
|
| 148 | error = -EIO;
|
| 149 | break;
|
| 150 | }
|
| 151 |
|
| 152 | avail = min(length, ((int)bvec->bv_len) - offset);
|
| 153 | data = bvec_virt(bvec);
|
| 154 | length -= avail;
|
| 155 | stream->buf.in = data + offset;
|
| 156 | stream->buf.in_size = avail;
|
| 157 | stream->buf.in_pos = 0;
|
| 158 | offset = 0;
|
| 159 | }
|
| 160 |
|
| 161 | if (stream->buf.out_pos == stream->buf.out_size) {
|
| 162 | stream->buf.out = squashfs_next_page(output);
|
| 163 | if (IS_ERR(stream->buf.out)) {
|
| 164 | error = PTR_ERR(stream->buf.out);
|
| 165 | break;
|
| 166 | } else if (stream->buf.out != NULL) {
|
| 167 | stream->buf.out_pos = 0;
|
| 168 | total += PAGE_SIZE;
|
| 169 | }
|
| 170 | }
|
| 171 |
|
| 172 | xz_err = xz_dec_run(stream->state, &stream->buf);
|
| 173 | if (xz_err == XZ_STREAM_END)
|
| 174 | break;
|
| 175 | if (xz_err != XZ_OK) {
|
| 176 | error = -EIO;
|
| 177 | break;
|
| 178 | }
|
| 179 | }
|
| 180 |
|
| 181 | finish:
|
| 182 | squashfs_finish_page(output);
|
| 183 |
|
| 184 | return error ? error : total + stream->buf.out_pos;
|
| 185 | }
|
| 186 |
|
| 187 | const struct squashfs_decompressor squashfs_xz_comp_ops = {
|
| 188 | .init = squashfs_xz_init,
|
| 189 | .comp_opts = squashfs_xz_comp_opts,
|
| 190 | .free = squashfs_xz_free,
|
| 191 | .decompress = squashfs_xz_uncompress,
|
| 192 | .id = XZ_COMPRESSION,
|
| 193 | .name = "xz",
|
| 194 | .alloc_buffer = 1,
|
| 195 | .supported = 1
|
| 196 | };
|