162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 Oracle. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci#include <linux/bio.h> 1462306a36Sopenharmony_ci#include <linux/lzo.h> 1562306a36Sopenharmony_ci#include <linux/refcount.h> 1662306a36Sopenharmony_ci#include "messages.h" 1762306a36Sopenharmony_ci#include "compression.h" 1862306a36Sopenharmony_ci#include "ctree.h" 1962306a36Sopenharmony_ci#include "super.h" 2062306a36Sopenharmony_ci#include "btrfs_inode.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define LZO_LEN 4 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Btrfs LZO compression format 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Regular and inlined LZO compressed data extents consist of: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * 1. Header 3062306a36Sopenharmony_ci * Fixed size. LZO_LEN (4) bytes long, LE32. 3162306a36Sopenharmony_ci * Records the total size (including the header) of compressed data. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * 2. Segment(s) 3462306a36Sopenharmony_ci * Variable size. Each segment includes one segment header, followed by data 3562306a36Sopenharmony_ci * payload. 3662306a36Sopenharmony_ci * One regular LZO compressed extent can have one or more segments. 3762306a36Sopenharmony_ci * For inlined LZO compressed extent, only one segment is allowed. 3862306a36Sopenharmony_ci * One segment represents at most one sector of uncompressed data. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * 2.1 Segment header 4162306a36Sopenharmony_ci * Fixed size. LZO_LEN (4) bytes long, LE32. 4262306a36Sopenharmony_ci * Records the total size of the segment (not including the header). 4362306a36Sopenharmony_ci * Segment header never crosses sector boundary, thus it's possible to 4462306a36Sopenharmony_ci * have at most 3 padding zeros at the end of the sector. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * 2.2 Data Payload 4762306a36Sopenharmony_ci * Variable size. Size up limit should be lzo1x_worst_compress(sectorsize) 4862306a36Sopenharmony_ci * which is 4419 for a 4KiB sectorsize. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Example with 4K sectorsize: 5162306a36Sopenharmony_ci * Page 1: 5262306a36Sopenharmony_ci * 0 0x2 0x4 0x6 0x8 0xa 0xc 0xe 0x10 5362306a36Sopenharmony_ci * 0x0000 | Header | SegHdr 01 | Data payload 01 ... | 5462306a36Sopenharmony_ci * ... 5562306a36Sopenharmony_ci * 0x0ff0 | SegHdr N | Data payload N ... |00| 5662306a36Sopenharmony_ci * ^^ padding zeros 5762306a36Sopenharmony_ci * Page 2: 5862306a36Sopenharmony_ci * 0x1000 | SegHdr N+1| Data payload N+1 ... | 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define WORKSPACE_BUF_LENGTH (lzo1x_worst_compress(PAGE_SIZE)) 6262306a36Sopenharmony_ci#define WORKSPACE_CBUF_LENGTH (lzo1x_worst_compress(PAGE_SIZE)) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct workspace { 6562306a36Sopenharmony_ci void *mem; 6662306a36Sopenharmony_ci void *buf; /* where decompressed data goes */ 6762306a36Sopenharmony_ci void *cbuf; /* where compressed data goes */ 6862306a36Sopenharmony_ci struct list_head list; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic struct workspace_manager wsm; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_civoid lzo_free_workspace(struct list_head *ws) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci kvfree(workspace->buf); 7862306a36Sopenharmony_ci kvfree(workspace->cbuf); 7962306a36Sopenharmony_ci kvfree(workspace->mem); 8062306a36Sopenharmony_ci kfree(workspace); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct list_head *lzo_alloc_workspace(unsigned int level) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct workspace *workspace; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); 8862306a36Sopenharmony_ci if (!workspace) 8962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci workspace->mem = kvmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL | __GFP_NOWARN); 9262306a36Sopenharmony_ci workspace->buf = kvmalloc(WORKSPACE_BUF_LENGTH, GFP_KERNEL | __GFP_NOWARN); 9362306a36Sopenharmony_ci workspace->cbuf = kvmalloc(WORKSPACE_CBUF_LENGTH, GFP_KERNEL | __GFP_NOWARN); 9462306a36Sopenharmony_ci if (!workspace->mem || !workspace->buf || !workspace->cbuf) 9562306a36Sopenharmony_ci goto fail; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci INIT_LIST_HEAD(&workspace->list); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return &workspace->list; 10062306a36Sopenharmony_cifail: 10162306a36Sopenharmony_ci lzo_free_workspace(&workspace->list); 10262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic inline void write_compress_length(char *buf, size_t len) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci __le32 dlen; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci dlen = cpu_to_le32(len); 11062306a36Sopenharmony_ci memcpy(buf, &dlen, LZO_LEN); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline size_t read_compress_length(const char *buf) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci __le32 dlen; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci memcpy(&dlen, buf, LZO_LEN); 11862306a36Sopenharmony_ci return le32_to_cpu(dlen); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * Will do: 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * - Write a segment header into the destination 12562306a36Sopenharmony_ci * - Copy the compressed buffer into the destination 12662306a36Sopenharmony_ci * - Make sure we have enough space in the last sector to fit a segment header 12762306a36Sopenharmony_ci * If not, we will pad at most (LZO_LEN (4)) - 1 bytes of zeros. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Will allocate new pages when needed. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic int copy_compressed_data_to_page(char *compressed_data, 13262306a36Sopenharmony_ci size_t compressed_size, 13362306a36Sopenharmony_ci struct page **out_pages, 13462306a36Sopenharmony_ci unsigned long max_nr_page, 13562306a36Sopenharmony_ci u32 *cur_out, 13662306a36Sopenharmony_ci const u32 sectorsize) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u32 sector_bytes_left; 13962306a36Sopenharmony_ci u32 orig_out; 14062306a36Sopenharmony_ci struct page *cur_page; 14162306a36Sopenharmony_ci char *kaddr; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if ((*cur_out / PAGE_SIZE) >= max_nr_page) 14462306a36Sopenharmony_ci return -E2BIG; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * We never allow a segment header crossing sector boundary, previous 14862306a36Sopenharmony_ci * run should ensure we have enough space left inside the sector. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci ASSERT((*cur_out / sectorsize) == (*cur_out + LZO_LEN - 1) / sectorsize); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci cur_page = out_pages[*cur_out / PAGE_SIZE]; 15362306a36Sopenharmony_ci /* Allocate a new page */ 15462306a36Sopenharmony_ci if (!cur_page) { 15562306a36Sopenharmony_ci cur_page = alloc_page(GFP_NOFS); 15662306a36Sopenharmony_ci if (!cur_page) 15762306a36Sopenharmony_ci return -ENOMEM; 15862306a36Sopenharmony_ci out_pages[*cur_out / PAGE_SIZE] = cur_page; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci kaddr = kmap_local_page(cur_page); 16262306a36Sopenharmony_ci write_compress_length(kaddr + offset_in_page(*cur_out), 16362306a36Sopenharmony_ci compressed_size); 16462306a36Sopenharmony_ci *cur_out += LZO_LEN; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci orig_out = *cur_out; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Copy compressed data */ 16962306a36Sopenharmony_ci while (*cur_out - orig_out < compressed_size) { 17062306a36Sopenharmony_ci u32 copy_len = min_t(u32, sectorsize - *cur_out % sectorsize, 17162306a36Sopenharmony_ci orig_out + compressed_size - *cur_out); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci kunmap_local(kaddr); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if ((*cur_out / PAGE_SIZE) >= max_nr_page) 17662306a36Sopenharmony_ci return -E2BIG; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci cur_page = out_pages[*cur_out / PAGE_SIZE]; 17962306a36Sopenharmony_ci /* Allocate a new page */ 18062306a36Sopenharmony_ci if (!cur_page) { 18162306a36Sopenharmony_ci cur_page = alloc_page(GFP_NOFS); 18262306a36Sopenharmony_ci if (!cur_page) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci out_pages[*cur_out / PAGE_SIZE] = cur_page; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci kaddr = kmap_local_page(cur_page); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci memcpy(kaddr + offset_in_page(*cur_out), 18962306a36Sopenharmony_ci compressed_data + *cur_out - orig_out, copy_len); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci *cur_out += copy_len; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * Check if we can fit the next segment header into the remaining space 19662306a36Sopenharmony_ci * of the sector. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci sector_bytes_left = round_up(*cur_out, sectorsize) - *cur_out; 19962306a36Sopenharmony_ci if (sector_bytes_left >= LZO_LEN || sector_bytes_left == 0) 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* The remaining size is not enough, pad it with zeros */ 20362306a36Sopenharmony_ci memset(kaddr + offset_in_page(*cur_out), 0, 20462306a36Sopenharmony_ci sector_bytes_left); 20562306a36Sopenharmony_ci *cur_out += sector_bytes_left; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciout: 20862306a36Sopenharmony_ci kunmap_local(kaddr); 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint lzo_compress_pages(struct list_head *ws, struct address_space *mapping, 21362306a36Sopenharmony_ci u64 start, struct page **pages, unsigned long *out_pages, 21462306a36Sopenharmony_ci unsigned long *total_in, unsigned long *total_out) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 21762306a36Sopenharmony_ci const u32 sectorsize = btrfs_sb(mapping->host->i_sb)->sectorsize; 21862306a36Sopenharmony_ci struct page *page_in = NULL; 21962306a36Sopenharmony_ci char *sizes_ptr; 22062306a36Sopenharmony_ci const unsigned long max_nr_page = *out_pages; 22162306a36Sopenharmony_ci int ret = 0; 22262306a36Sopenharmony_ci /* Points to the file offset of input data */ 22362306a36Sopenharmony_ci u64 cur_in = start; 22462306a36Sopenharmony_ci /* Points to the current output byte */ 22562306a36Sopenharmony_ci u32 cur_out = 0; 22662306a36Sopenharmony_ci u32 len = *total_out; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ASSERT(max_nr_page > 0); 22962306a36Sopenharmony_ci *out_pages = 0; 23062306a36Sopenharmony_ci *total_out = 0; 23162306a36Sopenharmony_ci *total_in = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * Skip the header for now, we will later come back and write the total 23562306a36Sopenharmony_ci * compressed size 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci cur_out += LZO_LEN; 23862306a36Sopenharmony_ci while (cur_in < start + len) { 23962306a36Sopenharmony_ci char *data_in; 24062306a36Sopenharmony_ci const u32 sectorsize_mask = sectorsize - 1; 24162306a36Sopenharmony_ci u32 sector_off = (cur_in - start) & sectorsize_mask; 24262306a36Sopenharmony_ci u32 in_len; 24362306a36Sopenharmony_ci size_t out_len; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Get the input page first */ 24662306a36Sopenharmony_ci if (!page_in) { 24762306a36Sopenharmony_ci page_in = find_get_page(mapping, cur_in >> PAGE_SHIFT); 24862306a36Sopenharmony_ci ASSERT(page_in); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Compress at most one sector of data each time */ 25262306a36Sopenharmony_ci in_len = min_t(u32, start + len - cur_in, sectorsize - sector_off); 25362306a36Sopenharmony_ci ASSERT(in_len); 25462306a36Sopenharmony_ci data_in = kmap_local_page(page_in); 25562306a36Sopenharmony_ci ret = lzo1x_1_compress(data_in + 25662306a36Sopenharmony_ci offset_in_page(cur_in), in_len, 25762306a36Sopenharmony_ci workspace->cbuf, &out_len, 25862306a36Sopenharmony_ci workspace->mem); 25962306a36Sopenharmony_ci kunmap_local(data_in); 26062306a36Sopenharmony_ci if (ret < 0) { 26162306a36Sopenharmony_ci pr_debug("BTRFS: lzo in loop returned %d\n", ret); 26262306a36Sopenharmony_ci ret = -EIO; 26362306a36Sopenharmony_ci goto out; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = copy_compressed_data_to_page(workspace->cbuf, out_len, 26762306a36Sopenharmony_ci pages, max_nr_page, 26862306a36Sopenharmony_ci &cur_out, sectorsize); 26962306a36Sopenharmony_ci if (ret < 0) 27062306a36Sopenharmony_ci goto out; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci cur_in += in_len; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Check if we're making it bigger after two sectors. And if 27662306a36Sopenharmony_ci * it is so, give up. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (cur_in - start > sectorsize * 2 && cur_in - start < cur_out) { 27962306a36Sopenharmony_ci ret = -E2BIG; 28062306a36Sopenharmony_ci goto out; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Check if we have reached page boundary */ 28462306a36Sopenharmony_ci if (PAGE_ALIGNED(cur_in)) { 28562306a36Sopenharmony_ci put_page(page_in); 28662306a36Sopenharmony_ci page_in = NULL; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Store the size of all chunks of compressed data */ 29162306a36Sopenharmony_ci sizes_ptr = kmap_local_page(pages[0]); 29262306a36Sopenharmony_ci write_compress_length(sizes_ptr, cur_out); 29362306a36Sopenharmony_ci kunmap_local(sizes_ptr); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = 0; 29662306a36Sopenharmony_ci *total_out = cur_out; 29762306a36Sopenharmony_ci *total_in = cur_in - start; 29862306a36Sopenharmony_ciout: 29962306a36Sopenharmony_ci if (page_in) 30062306a36Sopenharmony_ci put_page(page_in); 30162306a36Sopenharmony_ci *out_pages = DIV_ROUND_UP(cur_out, PAGE_SIZE); 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * Copy the compressed segment payload into @dest. 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * For the payload there will be no padding, just need to do page switching. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_cistatic void copy_compressed_segment(struct compressed_bio *cb, 31162306a36Sopenharmony_ci char *dest, u32 len, u32 *cur_in) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci u32 orig_in = *cur_in; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci while (*cur_in < orig_in + len) { 31662306a36Sopenharmony_ci struct page *cur_page; 31762306a36Sopenharmony_ci u32 copy_len = min_t(u32, PAGE_SIZE - offset_in_page(*cur_in), 31862306a36Sopenharmony_ci orig_in + len - *cur_in); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ASSERT(copy_len); 32162306a36Sopenharmony_ci cur_page = cb->compressed_pages[*cur_in / PAGE_SIZE]; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci memcpy_from_page(dest + *cur_in - orig_in, cur_page, 32462306a36Sopenharmony_ci offset_in_page(*cur_in), copy_len); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci *cur_in += copy_len; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciint lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 33362306a36Sopenharmony_ci const struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info; 33462306a36Sopenharmony_ci const u32 sectorsize = fs_info->sectorsize; 33562306a36Sopenharmony_ci char *kaddr; 33662306a36Sopenharmony_ci int ret; 33762306a36Sopenharmony_ci /* Compressed data length, can be unaligned */ 33862306a36Sopenharmony_ci u32 len_in; 33962306a36Sopenharmony_ci /* Offset inside the compressed data */ 34062306a36Sopenharmony_ci u32 cur_in = 0; 34162306a36Sopenharmony_ci /* Bytes decompressed so far */ 34262306a36Sopenharmony_ci u32 cur_out = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci kaddr = kmap_local_page(cb->compressed_pages[0]); 34562306a36Sopenharmony_ci len_in = read_compress_length(kaddr); 34662306a36Sopenharmony_ci kunmap_local(kaddr); 34762306a36Sopenharmony_ci cur_in += LZO_LEN; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * LZO header length check 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * The total length should not exceed the maximum extent length, 35362306a36Sopenharmony_ci * and all sectors should be used. 35462306a36Sopenharmony_ci * If this happens, it means the compressed extent is corrupted. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (len_in > min_t(size_t, BTRFS_MAX_COMPRESSED, cb->compressed_len) || 35762306a36Sopenharmony_ci round_up(len_in, sectorsize) < cb->compressed_len) { 35862306a36Sopenharmony_ci btrfs_err(fs_info, 35962306a36Sopenharmony_ci "invalid lzo header, lzo len %u compressed len %u", 36062306a36Sopenharmony_ci len_in, cb->compressed_len); 36162306a36Sopenharmony_ci return -EUCLEAN; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Go through each lzo segment */ 36562306a36Sopenharmony_ci while (cur_in < len_in) { 36662306a36Sopenharmony_ci struct page *cur_page; 36762306a36Sopenharmony_ci /* Length of the compressed segment */ 36862306a36Sopenharmony_ci u32 seg_len; 36962306a36Sopenharmony_ci u32 sector_bytes_left; 37062306a36Sopenharmony_ci size_t out_len = lzo1x_worst_compress(sectorsize); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * We should always have enough space for one segment header 37462306a36Sopenharmony_ci * inside current sector. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci ASSERT(cur_in / sectorsize == 37762306a36Sopenharmony_ci (cur_in + LZO_LEN - 1) / sectorsize); 37862306a36Sopenharmony_ci cur_page = cb->compressed_pages[cur_in / PAGE_SIZE]; 37962306a36Sopenharmony_ci ASSERT(cur_page); 38062306a36Sopenharmony_ci kaddr = kmap_local_page(cur_page); 38162306a36Sopenharmony_ci seg_len = read_compress_length(kaddr + offset_in_page(cur_in)); 38262306a36Sopenharmony_ci kunmap_local(kaddr); 38362306a36Sopenharmony_ci cur_in += LZO_LEN; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (seg_len > WORKSPACE_CBUF_LENGTH) { 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * seg_len shouldn't be larger than we have allocated 38862306a36Sopenharmony_ci * for workspace->cbuf 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci btrfs_err(fs_info, "unexpectedly large lzo segment len %u", 39162306a36Sopenharmony_ci seg_len); 39262306a36Sopenharmony_ci return -EIO; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Copy the compressed segment payload into workspace */ 39662306a36Sopenharmony_ci copy_compressed_segment(cb, workspace->cbuf, seg_len, &cur_in); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Decompress the data */ 39962306a36Sopenharmony_ci ret = lzo1x_decompress_safe(workspace->cbuf, seg_len, 40062306a36Sopenharmony_ci workspace->buf, &out_len); 40162306a36Sopenharmony_ci if (ret != LZO_E_OK) { 40262306a36Sopenharmony_ci btrfs_err(fs_info, "failed to decompress"); 40362306a36Sopenharmony_ci return -EIO; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Copy the data into inode pages */ 40762306a36Sopenharmony_ci ret = btrfs_decompress_buf2page(workspace->buf, out_len, cb, cur_out); 40862306a36Sopenharmony_ci cur_out += out_len; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* All data read, exit */ 41162306a36Sopenharmony_ci if (ret == 0) 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci ret = 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Check if the sector has enough space for a segment header */ 41662306a36Sopenharmony_ci sector_bytes_left = sectorsize - (cur_in % sectorsize); 41762306a36Sopenharmony_ci if (sector_bytes_left >= LZO_LEN) 41862306a36Sopenharmony_ci continue; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Skip the padding zeros */ 42162306a36Sopenharmony_ci cur_in += sector_bytes_left; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciint lzo_decompress(struct list_head *ws, const u8 *data_in, 42862306a36Sopenharmony_ci struct page *dest_page, unsigned long start_byte, size_t srclen, 42962306a36Sopenharmony_ci size_t destlen) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 43262306a36Sopenharmony_ci size_t in_len; 43362306a36Sopenharmony_ci size_t out_len; 43462306a36Sopenharmony_ci size_t max_segment_len = WORKSPACE_BUF_LENGTH; 43562306a36Sopenharmony_ci int ret = 0; 43662306a36Sopenharmony_ci char *kaddr; 43762306a36Sopenharmony_ci unsigned long bytes; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2) 44062306a36Sopenharmony_ci return -EUCLEAN; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci in_len = read_compress_length(data_in); 44362306a36Sopenharmony_ci if (in_len != srclen) 44462306a36Sopenharmony_ci return -EUCLEAN; 44562306a36Sopenharmony_ci data_in += LZO_LEN; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci in_len = read_compress_length(data_in); 44862306a36Sopenharmony_ci if (in_len != srclen - LZO_LEN * 2) { 44962306a36Sopenharmony_ci ret = -EUCLEAN; 45062306a36Sopenharmony_ci goto out; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci data_in += LZO_LEN; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci out_len = PAGE_SIZE; 45562306a36Sopenharmony_ci ret = lzo1x_decompress_safe(data_in, in_len, workspace->buf, &out_len); 45662306a36Sopenharmony_ci if (ret != LZO_E_OK) { 45762306a36Sopenharmony_ci pr_warn("BTRFS: decompress failed!\n"); 45862306a36Sopenharmony_ci ret = -EIO; 45962306a36Sopenharmony_ci goto out; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (out_len < start_byte) { 46362306a36Sopenharmony_ci ret = -EIO; 46462306a36Sopenharmony_ci goto out; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * the caller is already checking against PAGE_SIZE, but lets 46962306a36Sopenharmony_ci * move this check closer to the memcpy/memset 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci destlen = min_t(unsigned long, destlen, PAGE_SIZE); 47262306a36Sopenharmony_ci bytes = min_t(unsigned long, destlen, out_len - start_byte); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci kaddr = kmap_local_page(dest_page); 47562306a36Sopenharmony_ci memcpy(kaddr, workspace->buf + start_byte, bytes); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * btrfs_getblock is doing a zero on the tail of the page too, 47962306a36Sopenharmony_ci * but this will cover anything missing from the decompressed 48062306a36Sopenharmony_ci * data. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (bytes < destlen) 48362306a36Sopenharmony_ci memset(kaddr+bytes, 0, destlen-bytes); 48462306a36Sopenharmony_ci kunmap_local(kaddr); 48562306a36Sopenharmony_ciout: 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciconst struct btrfs_compress_op btrfs_lzo_compress = { 49062306a36Sopenharmony_ci .workspace_manager = &wsm, 49162306a36Sopenharmony_ci .max_level = 1, 49262306a36Sopenharmony_ci .default_level = 1, 49362306a36Sopenharmony_ci}; 494