Files

478 lines
12 KiB
C
Raw Permalink Normal View History

2021-11-16 18:36:02 -03:00
// SPDX-License-Identifier: GPL-2.0-only
2021-04-07 17:43:17 -03:00
/*
* Copyright (C) 2020 Corellium LLC
*/
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/mutex.h>
#include "apfs.h"
2023-01-18 19:23:36 -03:00
#include "libzbitmap.h"
2023-01-25 19:08:44 -03:00
#include "lzfse/lzfse.h"
2023-01-23 21:56:27 -03:00
#include "lzfse/lzvn_decode_base.h"
2021-04-07 17:43:17 -03:00
2021-07-31 20:11:18 -03:00
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
#include <linux/vmalloc.h>
static inline void *kvmalloc(size_t size, gfp_t flags)
{
gfp_t kmalloc_flags = flags;
void *ret;
if ((flags & GFP_KERNEL) != GFP_KERNEL)
return kmalloc(size, flags);
if (size > PAGE_SIZE)
kmalloc_flags |= __GFP_NOWARN | __GFP_NORETRY;
ret = kmalloc(size, flags);
if (ret || size < PAGE_SIZE)
return ret;
return vmalloc(size);
}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */
2021-04-07 17:43:17 -03:00
struct apfs_compress_file_data {
struct apfs_compress_hdr hdr;
u8 *buf;
ssize_t bufblk;
size_t bufsize;
struct mutex mtx;
2023-03-24 22:07:47 -03:00
struct super_block *sb;
struct apfs_compressed_data cdata;
2021-04-07 17:43:17 -03:00
};
static inline int apfs_compress_is_rsrc(u32 algo)
{
2023-01-18 21:56:04 -03:00
return (algo & 1) == 0;
}
static inline bool apfs_compress_is_supported(u32 algo)
{
2023-10-26 20:57:37 -03:00
switch (algo) {
2023-01-18 21:56:04 -03:00
case APFS_COMPRESS_ZLIB_RSRC:
case APFS_COMPRESS_ZLIB_ATTR:
2023-01-23 21:56:27 -03:00
case APFS_COMPRESS_LZVN_RSRC:
case APFS_COMPRESS_LZVN_ATTR:
2023-01-18 21:56:04 -03:00
case APFS_COMPRESS_PLAIN_RSRC:
case APFS_COMPRESS_PLAIN_ATTR:
2023-01-25 19:08:44 -03:00
case APFS_COMPRESS_LZFSE_RSRC:
case APFS_COMPRESS_LZFSE_ATTR:
2023-01-18 21:56:04 -03:00
case APFS_COMPRESS_LZBITMAP_RSRC:
2023-01-26 17:11:58 -03:00
case APFS_COMPRESS_LZBITMAP_ATTR:
2023-01-18 21:56:04 -03:00
return true;
default:
/* Once will usually be enough, don't flood the console */
pr_err_once("APFS: unsupported compression algorithm (%u)\n", algo);
return false;
}
2021-04-07 17:43:17 -03:00
}
static int apfs_compress_file_open(struct inode *inode, struct file *filp)
{
2023-03-24 22:07:47 -03:00
struct super_block *sb = inode->i_sb;
2023-10-24 21:30:08 -03:00
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
2021-04-07 17:43:17 -03:00
struct apfs_compress_file_data *fd;
ssize_t res;
bool is_rsrc;
2021-04-07 17:43:17 -03:00
/*
* The official implementation seems to transparently decompress files
* when you write to them. Doing that atomically inside the kernel is
* probably a chore, so for now I'll just leave it to the user to make
* an uncompressed copy themselves and replace the original. I might
* fix this in the future, but only if people complain (TODO).
*/
if (filp->f_mode & FMODE_WRITE) {
apfs_warn(sb, "writes to compressed files are not supported");
apfs_warn(sb, "you can work with a copy of the file instead");
return -EOPNOTSUPP;
}
2021-04-07 17:43:17 -03:00
if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
return -EOVERFLOW;
fd = kzalloc(sizeof(*fd), GFP_KERNEL);
2023-10-26 20:57:37 -03:00
if (!fd)
2021-04-07 17:43:17 -03:00
return -ENOMEM;
mutex_init(&fd->mtx);
2023-03-24 22:07:47 -03:00
fd->sb = sb;
2021-04-07 17:43:17 -03:00
2023-10-24 21:30:08 -03:00
down_read(&nxi->nx_big_sem);
2021-04-07 17:43:17 -03:00
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, &fd->hdr, sizeof(fd->hdr), 0);
2023-10-26 20:57:37 -03:00
if (res != sizeof(fd->hdr)) {
2023-03-24 22:07:47 -03:00
apfs_err(sb, "decmpfs header read failed");
2021-04-07 17:43:17 -03:00
goto fail;
2023-03-24 22:07:47 -03:00
}
2021-04-07 17:43:17 -03:00
2023-10-26 20:57:37 -03:00
if (!apfs_compress_is_supported(le32_to_cpu(fd->hdr.algo))) {
2023-01-18 21:56:04 -03:00
res = -EOPNOTSUPP;
goto fail;
}
fd->buf = kvmalloc(APFS_COMPRESS_BLOCK, GFP_KERNEL);
2023-10-26 20:57:37 -03:00
if (!fd->buf) {
2023-10-24 20:42:15 -03:00
res = -ENOMEM;
goto fail;
}
fd->bufblk = -1;
2021-04-07 17:43:17 -03:00
is_rsrc = apfs_compress_is_rsrc(le32_to_cpu(fd->hdr.algo));
res = apfs_xattr_get_compressed_data(inode, is_rsrc ? APFS_XATTR_NAME_RSRC_FORK : APFS_XATTR_NAME_COMPRESSED, &fd->cdata);
if (res) {
apfs_err(sb, "failed to get compressed data");
goto fail;
2021-04-07 17:43:17 -03:00
}
2023-10-24 21:30:08 -03:00
up_read(&nxi->nx_big_sem);
2021-04-07 17:43:17 -03:00
filp->private_data = fd;
return 0;
fail:
apfs_release_compressed_data(&fd->cdata);
2023-10-26 20:57:37 -03:00
if (fd->buf)
2021-04-07 17:43:17 -03:00
kvfree(fd->buf);
2023-10-24 21:30:08 -03:00
up_read(&nxi->nx_big_sem);
2021-04-07 17:43:17 -03:00
kfree(fd);
2023-10-26 20:57:37 -03:00
if (res > 0)
2021-04-07 17:43:17 -03:00
res = -EINVAL;
return res;
}
static int apfs_compress_file_read_block(struct apfs_compress_file_data *fd, loff_t block)
2021-04-07 17:43:17 -03:00
{
2023-03-24 22:07:47 -03:00
struct super_block *sb = fd->sb;
struct apfs_compressed_data *comp_data = &fd->cdata;
u8 *cdata = NULL;
u8 *tmp = fd->buf;
2023-01-18 19:23:36 -03:00
u32 doffs = 0, coffs;
2021-04-07 17:43:17 -03:00
size_t csize, bsize;
int res = 0;
2023-10-26 20:57:37 -03:00
if (apfs_compress_is_rsrc(le32_to_cpu(fd->hdr.algo)) &&
le32_to_cpu(fd->hdr.algo) != APFS_COMPRESS_LZBITMAP_RSRC &&
le32_to_cpu(fd->hdr.algo) != APFS_COMPRESS_LZVN_RSRC &&
le32_to_cpu(fd->hdr.algo) != APFS_COMPRESS_LZFSE_RSRC) {
struct apfs_compress_rsrc_hdr hdr = {0};
struct apfs_compress_rsrc_data cd = {0};
struct apfs_compress_rsrc_block blk = {0};
u32 blk_off;
res = apfs_compressed_data_read(comp_data, &hdr, sizeof(hdr), 0 /* offset */);
2023-10-26 20:57:37 -03:00
if (res) {
apfs_err(sb, "failed to read resource header");
return res;
}
doffs = be32_to_cpu(hdr.data_offs);
res = apfs_compressed_data_read(comp_data, &cd, sizeof(cd), doffs);
2023-10-26 20:57:37 -03:00
if (res) {
apfs_err(sb, "failed to read resource data header");
return res;
}
2023-10-26 20:57:37 -03:00
if (block >= le32_to_cpu(cd.num))
return 0;
blk_off = doffs + sizeof(cd) + sizeof(blk) * block;
res = apfs_compressed_data_read(comp_data, &blk, sizeof(blk), blk_off);
2023-10-26 20:57:37 -03:00
if (res) {
apfs_err(sb, "failed to read resource block metadata");
return res;
}
bsize = le64_to_cpu(fd->hdr.size) - block * APFS_COMPRESS_BLOCK;
2023-10-26 20:57:37 -03:00
if (bsize > APFS_COMPRESS_BLOCK)
bsize = APFS_COMPRESS_BLOCK;
csize = le32_to_cpu(blk.size);
coffs = le32_to_cpu(blk.offs) + 4;
} else if (apfs_compress_is_rsrc(le32_to_cpu(fd->hdr.algo))) {
__le32 blks[2];
u32 blk_off;
blk_off = doffs + sizeof(__le32) * block;
res = apfs_compressed_data_read(comp_data, blks, sizeof(blks), blk_off);
2023-10-26 20:57:37 -03:00
if (res) {
apfs_err(sb, "failed to read resource block metadata");
return res;
}
bsize = le64_to_cpu(fd->hdr.size) - block * APFS_COMPRESS_BLOCK;
2023-10-26 20:57:37 -03:00
if (bsize > APFS_COMPRESS_BLOCK)
bsize = APFS_COMPRESS_BLOCK;
coffs = le32_to_cpu(blks[0]);
csize = le32_to_cpu(blks[1]) - coffs;
} else {
/*
* I think attr compression is only for single-block files, in
* fact none of these files ever seem to decompress to more than
* 2048 bytes.
*/
bsize = le64_to_cpu(fd->hdr.size);
if (block != 0 || bsize > APFS_COMPRESS_BLOCK) {
apfs_err(sb, "file too big for inline compression");
return -EFSCORRUPTED;
}
/* The first few bytes are the decmpfs header */
coffs = sizeof(struct apfs_compress_hdr);
csize = comp_data->size - sizeof(struct apfs_compress_hdr);
}
cdata = kvmalloc(csize, GFP_KERNEL);
2023-10-26 20:57:37 -03:00
if (!cdata)
return -ENOMEM;
res = apfs_compressed_data_read(comp_data, cdata, csize, doffs + coffs);
2023-10-26 20:57:37 -03:00
if (res) {
apfs_err(sb, "failed to read compressed block");
goto fail;
}
2023-10-26 20:57:37 -03:00
switch (le32_to_cpu(fd->hdr.algo)) {
case APFS_COMPRESS_ZLIB_RSRC:
case APFS_COMPRESS_ZLIB_ATTR:
2023-10-26 20:57:37 -03:00
if (cdata[0] == 0x78 && csize >= 2) {
res = zlib_inflate_blob(tmp, bsize, cdata + 2, csize - 2);
2023-10-26 20:57:37 -03:00
if (res <= 0) {
apfs_err(sb, "zlib decompression failed");
goto fail;
}
bsize = res;
res = 0;
2023-10-26 20:57:37 -03:00
} else if ((cdata[0] & 0x0F) == 0x0F) {
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
} else {
apfs_err(sb, "zlib decompression failed");
res = -EINVAL;
goto fail;
}
break;
case APFS_COMPRESS_LZVN_RSRC:
case APFS_COMPRESS_LZVN_ATTR:
2023-10-26 20:57:37 -03:00
if (cdata[0] == 0x06) {
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
} else {
lzvn_decoder_state dstate = {0};
dstate.src = cdata;
dstate.src_end = dstate.src + csize;
dstate.dst = dstate.dst_begin = tmp;
dstate.dst_end = dstate.dst + bsize;
lzvn_decode(&dstate);
bsize = dstate.dst - tmp;
}
break;
case APFS_COMPRESS_LZBITMAP_RSRC:
case APFS_COMPRESS_LZBITMAP_ATTR:
2023-10-26 20:57:37 -03:00
if (cdata[0] == 0x5a) {
res = zbm_decompress(tmp, bsize, cdata, csize, &bsize);
2023-10-26 20:57:37 -03:00
if (res < 0) {
apfs_err(sb, "lzbitmap decompression failed");
goto fail;
}
res = 0;
2023-10-26 20:57:37 -03:00
} else if ((cdata[0] & 0x0F) == 0x0F) {
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
} else {
apfs_err(sb, "lzbitmap decompression failed");
res = -EINVAL;
goto fail;
}
break;
case APFS_COMPRESS_LZFSE_RSRC:
case APFS_COMPRESS_LZFSE_ATTR:
2023-10-26 20:57:37 -03:00
if (cdata[0] == 0x62 && csize >= 2) {
res = lzfse_decode_buffer(tmp, bsize, cdata, csize, NULL);
2023-10-26 20:57:37 -03:00
if (res == 0) {
apfs_err(sb, "lzfse decompression failed");
/* Could be ENOMEM too... */
res = -EINVAL;
goto fail;
}
bsize = res;
res = 0;
} else {
/* cdata[0] == 0xff, apparently */
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
}
break;
case APFS_COMPRESS_PLAIN_RSRC:
case APFS_COMPRESS_PLAIN_ATTR:
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
break;
default:
res = -EINVAL;
goto fail;
}
fd->bufblk = block;
fd->bufsize = bsize;
fail:
kvfree(cdata);
return res;
}
2021-04-07 17:43:17 -03:00
static int apfs_compress_file_release(struct inode *inode, struct file *filp)
{
struct apfs_compress_file_data *fd = filp->private_data;
apfs_release_compressed_data(&fd->cdata);
2023-10-26 20:57:37 -03:00
if (fd->buf)
2021-04-07 17:43:17 -03:00
kvfree(fd->buf);
kfree(fd);
return 0;
}
static ssize_t apfs_compress_file_read_from_block(struct apfs_compress_file_data *fd, char *buf, size_t size, loff_t off)
2023-10-05 21:18:45 -03:00
{
struct super_block *sb = fd->sb;
2023-10-24 21:30:08 -03:00
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_compressed_data cdata = fd->cdata;
2023-10-05 21:18:45 -03:00
loff_t block;
size_t bsize;
ssize_t res;
/*
* Request reads of all blocks before actually working with any of them.
* The compressed data is typically small enough that this is effective.
* It would be much better to make an inode for the xattr dstream and
* work with readahead as usual, but I'm not confident I can get that
* right (TODO).
*/
2023-10-24 21:30:08 -03:00
if (cdata.has_dstream && off == 0) {
down_read(&nxi->nx_big_sem);
apfs_nonsparse_dstream_preread(cdata.dstream);
2023-10-24 21:30:08 -03:00
up_read(&nxi->nx_big_sem);
}
2023-10-26 20:57:37 -03:00
if (off >= le64_to_cpu(fd->hdr.size))
2023-10-05 21:18:45 -03:00
return 0;
2023-10-26 20:57:37 -03:00
if (size > le64_to_cpu(fd->hdr.size) - off)
2023-10-05 21:18:45 -03:00
size = le64_to_cpu(fd->hdr.size) - off;
block = off / APFS_COMPRESS_BLOCK;
off -= block * APFS_COMPRESS_BLOCK;
2023-10-26 20:57:37 -03:00
if (block != fd->bufblk) {
2023-10-24 21:30:08 -03:00
down_read(&nxi->nx_big_sem);
2023-10-05 21:18:45 -03:00
res = apfs_compress_file_read_block(fd, block);
2023-10-24 21:30:08 -03:00
up_read(&nxi->nx_big_sem);
2023-10-05 21:18:45 -03:00
if (res) {
apfs_err(sb, "failed to read block into buffer");
return res;
}
}
bsize = fd->bufsize;
2023-10-26 20:57:37 -03:00
if (bsize < off)
2023-10-05 21:18:45 -03:00
return 0;
bsize -= off;
2023-10-26 20:57:37 -03:00
if (size > bsize)
2023-10-05 21:18:45 -03:00
size = bsize;
memcpy(buf, fd->buf + off, size);
return size;
}
static ssize_t apfs_compress_file_read_page(struct file *filp, char *buf, loff_t off)
{
struct apfs_compress_file_data *fd = filp->private_data;
loff_t step;
ssize_t block, res;
size_t size = PAGE_SIZE;
step = 0;
2023-10-26 20:57:37 -03:00
while (step < size) {
block = APFS_COMPRESS_BLOCK - ((off + step) & (APFS_COMPRESS_BLOCK - 1));
2023-10-26 20:57:37 -03:00
if (block > size - step)
block = size - step;
mutex_lock(&fd->mtx);
res = apfs_compress_file_read_from_block(fd, buf + step, block, off + step);
mutex_unlock(&fd->mtx);
2023-10-26 20:57:37 -03:00
if (res < block) {
if (res < 0 && !step)
return res;
step += res > 0 ? res : 0;
break;
2023-10-05 21:18:45 -03:00
}
step += block;
2023-10-05 21:18:45 -03:00
}
return step;
2023-10-05 21:18:45 -03:00
}
2025-02-28 15:08:41 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
2023-10-05 21:18:45 -03:00
static int apfs_compress_read_folio(struct file *filp, struct folio *folio)
{
struct page *page = &folio->page;
#else
static int apfs_compress_readpage(struct file *filp, struct page *page)
{
#endif
2024-03-27 20:57:02 -03:00
char *addr = NULL;
2023-10-05 21:18:45 -03:00
ssize_t ret;
loff_t off;
/* Mostly copied from ext4_read_inline_page() */
2025-07-01 20:45:23 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)
off = page_folio(page)->index << PAGE_SHIFT;
#else
2023-10-05 21:18:45 -03:00
off = page->index << PAGE_SHIFT;
2025-07-01 20:45:23 -03:00
#endif
2024-03-27 20:57:02 -03:00
addr = kmap(page);
2023-10-05 21:18:45 -03:00
ret = apfs_compress_file_read_page(filp, addr, off);
flush_dcache_page(page);
2024-03-27 20:57:02 -03:00
kunmap(page);
2023-10-05 21:18:45 -03:00
if (ret >= 0) {
zero_user_segment(page, ret, PAGE_SIZE);
SetPageUptodate(page);
2023-10-05 21:18:45 -03:00
ret = 0;
}
unlock_page(page);
return ret;
2023-10-05 21:18:45 -03:00
}
const struct address_space_operations apfs_compress_aops = {
2025-02-28 15:08:41 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
2023-10-05 21:18:45 -03:00
.read_folio = apfs_compress_read_folio,
#else
.readpage = apfs_compress_readpage,
#endif
};
/* TODO: these operations are all happening without proper locks */
2021-04-07 17:43:17 -03:00
const struct file_operations apfs_compress_file_operations = {
.open = apfs_compress_file_open,
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
2021-04-07 17:43:17 -03:00
.release = apfs_compress_file_release,
2023-10-06 20:14:44 -03:00
.mmap = apfs_file_mmap,
2021-04-07 17:43:17 -03:00
};
int apfs_compress_get_size(struct inode *inode, loff_t *size)
{
struct apfs_compress_hdr hdr;
int res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, &hdr, sizeof(hdr), 0);
2023-10-26 20:57:37 -03:00
if (res < 0)
2021-04-07 17:43:17 -03:00
return res;
2023-10-26 20:57:37 -03:00
if (res != sizeof(hdr)) {
2023-03-24 22:07:47 -03:00
apfs_err(inode->i_sb, "decmpfs header read failed");
2021-04-07 17:43:17 -03:00
return 1;
2023-03-24 22:07:47 -03:00
}
2021-04-07 17:43:17 -03:00
2023-10-26 20:57:37 -03:00
if (!apfs_compress_is_supported(le32_to_cpu(hdr.algo)))
2021-04-07 17:43:17 -03:00
return 1;
*size = le64_to_cpu(hdr.size);
return 0;
}