mirror of
https://github.com/linux-apfs/linux-apfs-rw.git
synced 2026-08-18 02:19:59 -07:00
Compare commits
165
Commits
v0.3.5
...
development
No files matched your search
@@ -9,3 +9,4 @@
|
||||
*.a
|
||||
*.mod
|
||||
tags
|
||||
version.h
|
||||
@@ -13,9 +13,16 @@ apfs-y := btree.o compress.o dir.o extents.o file.o inode.o key.o libzbitmap.o \
|
||||
lzfse/lzvn_decode_base.o message.o namei.o node.o object.o snapshot.o \
|
||||
spaceman.o super.o symlink.o transaction.o unicode.o xattr.o xfield.o
|
||||
|
||||
# If you want mounts to be writable by default, run the build as:
|
||||
# make APFS_CONFIG=-DCONFIG_APFS_RW_ALWAYS
|
||||
# This is risky and not generally recommended.
|
||||
ccflags-y += $(APFS_CONFIG)
|
||||
|
||||
default:
|
||||
./genver.sh
|
||||
make -C $(KERNEL_DIR) M=$(PWD)
|
||||
install:
|
||||
make -C $(KERNEL_DIR) M=$(PWD) modules_install
|
||||
clean:
|
||||
rm -f version.h
|
||||
make -C $(KERNEL_DIR) M=$(PWD) clean
|
||||
+33
-13
@@ -4,13 +4,8 @@ Apple File System for Linux
|
||||
|
||||
The Apple File System (APFS) is the copy-on-write filesystem currently used on
|
||||
all Apple devices. This module provides a degree of experimental support on
|
||||
Linux.
|
||||
|
||||
It's supposed to work with a range of kernel versions starting at 4.9 or before,
|
||||
but only a few of those have actually been tested. Also, kernel versions below
|
||||
4.12 will be slower for some operations. If you run into any problem, please
|
||||
send a report to <linux-apfs@googlegroups.com> or file a github issue at
|
||||
https://github.com/eafer/linux-apfs-rw/issues.
|
||||
Linux. It's intended to work with all kernel versions since 4.12, but testing
|
||||
is focused on 5.18 and above.
|
||||
|
||||
To help test write support, a set of userland tools is also under development.
|
||||
The git tree can be retrieved from https://github.com/eafer/apfsprogs.git.
|
||||
@@ -19,14 +14,32 @@ Known limitations
|
||||
=================
|
||||
|
||||
This module is the result of reverse engineering and testing has been limited.
|
||||
If you make use of the write support, expect data corruption. Please report any
|
||||
issues that you find, but I can't promise a quick resolution at this stage.
|
||||
If you make use of the write support, there is a real risk of data corruption,
|
||||
so mounts are read-only by default. Please report any issues that you find.
|
||||
|
||||
Encryption is not yet implemented even in read-only mode.
|
||||
Writes to fusion drives will likely never be supported, though I might
|
||||
reconsider this if people actually need it. Encryption is not yet implemented
|
||||
even in read-only mode.
|
||||
|
||||
Reporting bugs
|
||||
==============
|
||||
|
||||
If you encounter any problem, the first thing you should do is run (as root)::
|
||||
|
||||
dmesg | grep -i apfs
|
||||
|
||||
to see all the error messages. If that doesn't help you, please file a github
|
||||
issue at https://github.com/eafer/linux-apfs-rw/issues. Or send me an email if
|
||||
you prefer.
|
||||
|
||||
Build
|
||||
=====
|
||||
|
||||
Note that at this point the driver has been packaged for several distros, so
|
||||
it may be more convenient to install it from there instead of building it
|
||||
yourself. The distro packages are often outdated though, so be sure to check
|
||||
the version.
|
||||
|
||||
In order to build a module out-of-tree, you will first need the Linux kernel
|
||||
headers. Depending on your distro, you can get them by running (as root)::
|
||||
|
||||
@@ -44,10 +57,15 @@ Now you can just cd to the linux-apfs-rw directory and run::
|
||||
make
|
||||
|
||||
The resulting module is the apfs.ko file. Before you can use it you must insert
|
||||
it into the kernel, as well as its dependencies. Again as root::
|
||||
it into the kernel. Again as root::
|
||||
|
||||
insmod apfs.ko
|
||||
|
||||
If you get an "unknown symbol in module" error, run::
|
||||
|
||||
modprobe libcrc32c
|
||||
insmod apfs.ko
|
||||
|
||||
and then try the insmod command again.
|
||||
|
||||
Mount
|
||||
=====
|
||||
@@ -64,12 +82,14 @@ vol=n Volume number to mount. The default is volume 0.
|
||||
|
||||
snap=label Volume snapshot to mount (in read-only mode).
|
||||
|
||||
tier2=path Path to the tier 2 device. For fusion drives only.
|
||||
|
||||
uid=n, gid=n Override on-disk inode ownership data with given uid/gid.
|
||||
|
||||
cknodes Verify the checksum on all metadata nodes. Right now this has a
|
||||
severe performance cost, so it's not recommended.
|
||||
|
||||
readwrite Enable the experimental write support. This **will** corrupt your
|
||||
readwrite Enable the experimental write support. This may corrupt your
|
||||
container.
|
||||
============ =================================================================
|
||||
|
||||
|
||||
+56
-12
@@ -268,6 +268,10 @@ struct apfs_phys_ext_val {
|
||||
#define APFS_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL
|
||||
#define APFS_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL
|
||||
#define APFS_FILE_EXTENT_FLAG_SHIFT 56
|
||||
#define APFS_FILE_EXTENT_CRYPTO_FLAG 0x01 /* Made-up name */
|
||||
#define APFS_FILE_EXTENT_PREALLOCATED 0x02 /* Made-up name */
|
||||
#define APFS_VALID_FILE_EXTENT_FLAGS (APFS_FILE_EXTENT_CRYPTO_FLAG \
|
||||
| APFS_FILE_EXTENT_PREALLOCATED)
|
||||
|
||||
/*
|
||||
* Put a bound on maximum file size so that a growing truncation will always
|
||||
@@ -1051,13 +1055,19 @@ struct apfs_checkpoint_map_phys {
|
||||
#define APFS_FS_SPILLEDOVER 0x00000010LL
|
||||
#define APFS_FS_RUN_SPILLOVER_CLEANER 0x00000020LL
|
||||
#define APFS_FS_ALWAYS_CHECK_EXTENTREF 0x00000040LL
|
||||
#define APFS_FS_PREVIOUSLY_SEALED 0x00000080LL /* Made-up name */
|
||||
#define APFS_FS_PFK 0x00000100LL /* Made-up name */
|
||||
#define APFS_FS_UNKNOWN_200 0x00000200LL
|
||||
#define APFS_FS_FLAGS_VALID_MASK (APFS_FS_UNENCRYPTED \
|
||||
| APFS_FS_EFFACEABLE \
|
||||
| APFS_FS_RESERVED_4 \
|
||||
| APFS_FS_ONEKEY \
|
||||
| APFS_FS_SPILLEDOVER \
|
||||
| APFS_FS_RUN_SPILLOVER_CLEANER \
|
||||
| APFS_FS_ALWAYS_CHECK_EXTENTREF)
|
||||
| APFS_FS_EFFACEABLE \
|
||||
| APFS_FS_RESERVED_4 \
|
||||
| APFS_FS_ONEKEY \
|
||||
| APFS_FS_SPILLEDOVER \
|
||||
| APFS_FS_RUN_SPILLOVER_CLEANER \
|
||||
| APFS_FS_ALWAYS_CHECK_EXTENTREF \
|
||||
| APFS_FS_PREVIOUSLY_SEALED \
|
||||
| APFS_FS_PFK \
|
||||
| APFS_FS_UNKNOWN_200)
|
||||
|
||||
#define APFS_FS_CRYPTOFLAGS (APFS_FS_UNENCRYPTED \
|
||||
| APFS_FS_EFFACEABLE \
|
||||
@@ -1124,7 +1134,9 @@ struct apfs_checkpoint_map_phys {
|
||||
#define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE 0x00000008LL
|
||||
#define APFS_INCOMPAT_INCOMPLETE_RESTORE 0x00000010LL
|
||||
#define APFS_INCOMPAT_SEALED_VOLUME 0x00000020LL
|
||||
#define APFS_INCOMPAT_RESERVED_40 0x00000040LL
|
||||
#define APFS_INCOMPAT_PFK 0x00000040LL /* Made-up name */
|
||||
#define APFS_INCOMPAT_EXTENT_PREALLOC_FLAG 0x00000080LL /* Made-up name */
|
||||
#define APFS_INCOMPAT_SECONDARY_FSROOT 0x00000100LL /* Made-up name */
|
||||
|
||||
#define APFS_SUPPORTED_INCOMPAT_MASK (APFS_INCOMPAT_CASE_INSENSITIVE \
|
||||
| APFS_INCOMPAT_DATALESS_SNAPS \
|
||||
@@ -1132,7 +1144,9 @@ struct apfs_checkpoint_map_phys {
|
||||
| APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \
|
||||
| APFS_INCOMPAT_INCOMPLETE_RESTORE \
|
||||
| APFS_INCOMPAT_SEALED_VOLUME \
|
||||
| APFS_INCOMPAT_RESERVED_40)
|
||||
| APFS_INCOMPAT_PFK \
|
||||
| APFS_INCOMPAT_EXTENT_PREALLOC_FLAG \
|
||||
| APFS_INCOMPAT_SECONDARY_FSROOT)
|
||||
|
||||
#define APFS_MODIFIED_NAMELEN 32
|
||||
|
||||
@@ -1162,6 +1176,18 @@ struct apfs_modified_by {
|
||||
#define APFS_CRYPTO_RESERVED_5 5
|
||||
#define APFS_UNASSIGNED_CRYPTO_ID (~0ULL)
|
||||
|
||||
/* Doc id index flags. I'm making up the names for now. */
|
||||
#define APFS_DOC_ID_HAS_PREV_TREE 0x00000001
|
||||
#define APFS_DOC_ID_UNKNOWN_02 0x00000002
|
||||
#define APFS_DOC_ID_UNKNOWN_04 0x00000004
|
||||
#define APFS_DOC_ID_UNKNOWN_08 0x00000008
|
||||
#define APFS_DOC_ID_UNKNOWN_10 0x00000010
|
||||
#define APFS_DOC_ID_VALID_FLAGS (APFS_DOC_ID_HAS_PREV_TREE \
|
||||
| APFS_DOC_ID_UNKNOWN_02 \
|
||||
| APFS_DOC_ID_UNKNOWN_04 \
|
||||
| APFS_DOC_ID_UNKNOWN_08 \
|
||||
| APFS_DOC_ID_UNKNOWN_10)
|
||||
|
||||
/*
|
||||
* Structure used to store the encryption state
|
||||
*/
|
||||
@@ -1241,15 +1267,24 @@ struct apfs_superblock {
|
||||
|
||||
__le64 apfs_snap_meta_ext_oid;
|
||||
|
||||
char apfs_volume_group_id[UUID_SIZE];
|
||||
/*3F0*/ char apfs_volume_group_id[UUID_SIZE];
|
||||
|
||||
__le64 apfs_integrity_meta_oid;
|
||||
/*400*/ __le64 apfs_integrity_meta_oid;
|
||||
|
||||
__le64 apfs_fext_tree_oid;
|
||||
__le32 apfs_fext_tree_type;
|
||||
/*410*/ __le32 apfs_fext_tree_type;
|
||||
|
||||
__le32 reserved_type;
|
||||
__le64 reserved_oid;
|
||||
|
||||
/*420*/ __le64 apfs_doc_id_index_xid;
|
||||
__le32 apfs_doc_id_index_flags;
|
||||
__le32 apfs_doc_id_tree_type;
|
||||
/*430*/ __le64 apfs_doc_id_tree_oid; /* Made-up name */
|
||||
__le64 apfs_prev_doc_id_tree_oid;
|
||||
__le64 apfs_doc_id_fixup_cursor;
|
||||
__le64 apfs_sec_root_tree_oid;
|
||||
/*450*/ __le32 apfs_sec_root_tree_type;
|
||||
} __packed;
|
||||
|
||||
/* Extended attributes constants */
|
||||
@@ -1268,9 +1303,10 @@ enum {
|
||||
APFS_XATTR_DATA_EMBEDDED = 0x00000002,
|
||||
APFS_XATTR_FILE_SYSTEM_OWNED = 0x00000004,
|
||||
APFS_XATTR_RESERVED_8 = 0x00000008,
|
||||
APFS_XATTR_UNKNOWN_10 = 0x00000010,
|
||||
};
|
||||
|
||||
#define APFS_XATTR_VALID_FLAGS 0x0000000f
|
||||
#define APFS_XATTR_VALID_FLAGS 0x0000001f
|
||||
|
||||
/*
|
||||
* Structure of the value of an extended attributes record
|
||||
@@ -1438,6 +1474,8 @@ struct apfs_compress_rsrc_data {
|
||||
} __packed block[];
|
||||
} __packed;
|
||||
|
||||
#define APFS_FUSION_TIER2_DEVICE_BYTE_ADDR 0x4000000000000000ULL
|
||||
|
||||
/*
|
||||
* TODO: this is a placeholder, create some snapshots with the official
|
||||
* implementation to find the actual limit.
|
||||
@@ -1459,6 +1497,12 @@ struct apfs_snap_metadata_val {
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/* Snapshot metadata record flags */
|
||||
enum {
|
||||
APFS_SNAP_META_PENDING_DATALESS = 0x00000001,
|
||||
APFS_SNAP_MERGE_IN_PROGRESS = 0x00000002,
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure of the value of a snapshot name record
|
||||
*/
|
||||
|
||||
+9
-5
@@ -409,7 +409,7 @@ static ssize_t apfs_compress_file_read_page(struct file *filp, char *buf, loff_t
|
||||
return step;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
|
||||
static int apfs_compress_read_folio(struct file *filp, struct folio *folio)
|
||||
{
|
||||
struct page *page = &folio->page;
|
||||
@@ -417,16 +417,20 @@ static int apfs_compress_read_folio(struct file *filp, struct folio *folio)
|
||||
static int apfs_compress_readpage(struct file *filp, struct page *page)
|
||||
{
|
||||
#endif
|
||||
void *addr = NULL;
|
||||
char *addr = NULL;
|
||||
ssize_t ret;
|
||||
loff_t off;
|
||||
|
||||
/* Mostly copied from ext4_read_inline_page() */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)
|
||||
off = page_folio(page)->index << PAGE_SHIFT;
|
||||
#else
|
||||
off = page->index << PAGE_SHIFT;
|
||||
addr = kmap_atomic(page);
|
||||
#endif
|
||||
addr = kmap(page);
|
||||
ret = apfs_compress_file_read_page(filp, addr, off);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(addr);
|
||||
kunmap(page);
|
||||
if (ret >= 0) {
|
||||
zero_user_segment(page, ret, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
@@ -438,7 +442,7 @@ static int apfs_compress_readpage(struct file *filp, struct page *page)
|
||||
}
|
||||
|
||||
const struct address_space_operations apfs_compress_aops = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
|
||||
.read_folio = apfs_compress_read_folio,
|
||||
#else
|
||||
.readpage = apfs_compress_readpage,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.5"
|
||||
PACKAGE_VERSION="0.3.21"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
AUTOINSTALL="yes"
|
||||
PRE_BUILD="genver.sh"
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <linux/blk_types.h>
|
||||
#include "apfs.h"
|
||||
|
||||
#define MAX(X, Y) ((X) <= (Y) ? (Y) : (X))
|
||||
|
||||
/**
|
||||
* apfs_ext_is_hole - Does this extent represent a hole in a sparse file?
|
||||
* @extent: the extent to check
|
||||
@@ -139,7 +137,7 @@ static int apfs_extent_read(struct apfs_dstream_info *dstream, sector_t dsblock,
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = apfs_is_sealed(sb) ? APFS_QUERY_FEXT : APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -387,7 +385,7 @@ static int apfs_shrink_extent_tail(struct apfs_query *query, struct apfs_dstream
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_query_found_extent - Is this query pointing to an extent record?
|
||||
* apfs_query_found_extent - Did this query find an extent with the right id?
|
||||
* @query: the (successful) query that found the record
|
||||
*/
|
||||
static inline bool apfs_query_found_extent(struct apfs_query *query)
|
||||
@@ -398,7 +396,12 @@ static inline bool apfs_query_found_extent(struct apfs_query *query)
|
||||
if (query->key_len < sizeof(*hdr))
|
||||
return false;
|
||||
hdr = raw + query->key_off;
|
||||
return apfs_cat_type(hdr) == APFS_TYPE_FILE_EXTENT;
|
||||
|
||||
if (apfs_cat_type(hdr) != APFS_TYPE_FILE_EXTENT)
|
||||
return false;
|
||||
if (apfs_cat_cnid(hdr) != query->key.id)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +416,6 @@ static int apfs_update_tail_extent(struct apfs_dstream_info *dstream, const stru
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_file_extent_key raw_key;
|
||||
struct apfs_file_extent_val raw_val;
|
||||
@@ -431,13 +433,11 @@ static int apfs_update_tail_extent(struct apfs_dstream_info *dstream, const stru
|
||||
new_crypto = 0;
|
||||
raw_val.crypto_id = cpu_to_le64(new_crypto);
|
||||
|
||||
/* We want the last extent record */
|
||||
apfs_init_file_extent_key(extent_id, -1, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
/* We want the last extent record */
|
||||
apfs_init_file_extent_key(extent_id, -1, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -614,7 +614,7 @@ search_and_insert:
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -758,7 +758,6 @@ static int apfs_update_extent(struct apfs_dstream_info *dstream, const struct ap
|
||||
}
|
||||
return apfs_update_mid_extent(dstream, extent);
|
||||
}
|
||||
#define APFS_UPDATE_EXTENTS_MAXOPS (1 + 2 * APFS_CRYPTO_ADJ_REFCNT_MAXOPS())
|
||||
|
||||
static int apfs_extend_phys_extent(struct apfs_query *query, u64 bno, u64 blkcnt, u64 dstream_id)
|
||||
{
|
||||
@@ -801,7 +800,6 @@ static int apfs_insert_phys_extent(struct apfs_dstream_info *dstream, const stru
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
struct apfs_node *extref_root;
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_phys_extent pext;
|
||||
u64 blkcnt = extent->len >> sb->s_blocksize_bits;
|
||||
@@ -830,8 +828,7 @@ static int apfs_insert_phys_extent(struct apfs_dstream_info *dstream, const stru
|
||||
* one.
|
||||
*/
|
||||
last_bno = extent->phys_block_num + blkcnt - 1;
|
||||
apfs_init_extent_key(last_bno, &key);
|
||||
query->key = &key;
|
||||
apfs_init_extent_key(last_bno, &query->key);
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1138,7 +1135,6 @@ int apfs_flush_extent_cache(struct apfs_dstream_info *dstream)
|
||||
dstream->ds_ext_dirty = false;
|
||||
return 0;
|
||||
}
|
||||
#define APFS_FLUSH_EXTENT_CACHE APFS_UPDATE_EXTENTS_MAXOPS
|
||||
|
||||
/**
|
||||
* apfs_create_hole - Create and insert a hole extent for the dstream
|
||||
@@ -1153,7 +1149,6 @@ static int apfs_create_hole(struct apfs_dstream_info *dstream, u64 start, u64 en
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_file_extent_key raw_key;
|
||||
struct apfs_file_extent_val raw_val;
|
||||
@@ -1173,11 +1168,10 @@ static int apfs_create_hole(struct apfs_dstream_info *dstream, u64 start, u64 en
|
||||
raw_val.phys_block_num = cpu_to_le64(0); /* It's a hole... */
|
||||
raw_val.crypto_id = cpu_to_le64(apfs_vol_is_encrypted(sb) ? extent_id : 0);
|
||||
|
||||
apfs_init_file_extent_key(extent_id, start, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_file_extent_key(extent_id, start, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1260,7 +1254,6 @@ static int apfs_range_in_snap(struct super_block *sb, u64 bno, u64 blkcnt, bool
|
||||
{
|
||||
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
struct apfs_node *extref_root = NULL;
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_phys_extent pext = {0};
|
||||
int ret;
|
||||
@@ -1287,8 +1280,7 @@ static int apfs_range_in_snap(struct super_block *sb, u64 bno, u64 blkcnt, bool
|
||||
goto out;
|
||||
}
|
||||
|
||||
apfs_init_extent_key(bno, &key);
|
||||
query->key = &key;
|
||||
apfs_init_extent_key(bno, &query->key);
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1457,10 +1449,6 @@ static int apfs_dstream_get_new_block(struct apfs_dstream_info *dstream, u64 dsb
|
||||
dstream->ds_ext_dirty = true;
|
||||
return 0;
|
||||
}
|
||||
int APFS_GET_NEW_BLOCK_MAXOPS(void)
|
||||
{
|
||||
return APFS_FLUSH_EXTENT_CACHE;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_dstream_get_new_bno - Allocate a new block inside a dstream
|
||||
@@ -1500,18 +1488,15 @@ static int apfs_shrink_dstream_last_extent(struct apfs_dstream_info *dstream, lo
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_file_extent tail;
|
||||
u64 extent_id = dstream->ds_id;
|
||||
int ret = 0;
|
||||
|
||||
apfs_init_file_extent_key(extent_id, -1, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_file_extent_key(extent_id, -1, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1637,6 +1622,125 @@ int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size)
|
||||
return apfs_create_hole(dstream, old_blks, new_blks);
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_dstream_delete_front - Deletes as many leading extents as possible
|
||||
* @sb: filesystem superblock
|
||||
* @ds_id: id for the dstream to delete
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure, which may
|
||||
* be -ENODATA if there are no more extents, or -EAGAIN if the free queue is
|
||||
* getting too full.
|
||||
*/
|
||||
static int apfs_dstream_delete_front(struct super_block *sb, u64 ds_id)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
struct apfs_spaceman_free_queue *fq = NULL;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_file_extent head;
|
||||
bool first_match = true;
|
||||
int ret;
|
||||
|
||||
fq = &sm_raw->sm_fq[APFS_SFQ_MAIN];
|
||||
if (le64_to_cpu(fq->sfq_count) > APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
return -EAGAIN;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
apfs_init_file_extent_key(ds_id, 0, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
next_extent:
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret && ret != -ENODATA) {
|
||||
apfs_err(sb, "query failed for first extent of id 0x%llx", ds_id);
|
||||
goto out;
|
||||
}
|
||||
apfs_query_direct_forward(query);
|
||||
if (!apfs_query_found_extent(query)) {
|
||||
/*
|
||||
* After the original lookup, the query may not be set to the
|
||||
* first extent, but instead to the record that comes right
|
||||
* before.
|
||||
*/
|
||||
if (first_match) {
|
||||
first_match = false;
|
||||
goto next_extent;
|
||||
}
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
first_match = false;
|
||||
|
||||
ret = apfs_extent_from_query(query, &head);
|
||||
if (ret) {
|
||||
apfs_err(sb, "bad head extent record on dstream 0x%llx", ds_id);
|
||||
goto out;
|
||||
}
|
||||
ret = apfs_btree_remove(query);
|
||||
if (ret) {
|
||||
apfs_err(sb, "removal failed for id 0x%llx, addr 0x%llx", ds_id, head.logical_addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The official fsck doesn't complain about wrong sparse byte counts
|
||||
* for orphans, so I guess we don't need to update them here
|
||||
*/
|
||||
if (!apfs_ext_is_hole(&head)) {
|
||||
ret = apfs_range_put_reference(sb, head.phys_block_num, head.len);
|
||||
if (ret) {
|
||||
apfs_err(sb, "failed to put range 0x%llx-0x%llx", head.phys_block_num, head.len);
|
||||
goto out;
|
||||
}
|
||||
ret = apfs_crypto_adj_refcnt(sb, head.crypto_id, -1);
|
||||
if (ret) {
|
||||
apfs_err(sb, "failed to take crypto id 0x%llx", head.crypto_id);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (le64_to_cpu(fq->sfq_count) <= APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
goto next_extent;
|
||||
ret = -EAGAIN;
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_inode_delete_front - Deletes as many leading extents as possible
|
||||
* @inode: inode to delete
|
||||
*
|
||||
* Tries to delete all extents for @inode, in which case it returns 0. If the
|
||||
* free queue is getting too full, deletes as much as is reasonable and returns
|
||||
* -EAGAIN. May return other negative error codes as well.
|
||||
*/
|
||||
int apfs_inode_delete_front(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_dstream_info *dstream = NULL;
|
||||
struct apfs_inode_info *ai = APFS_I(inode);
|
||||
int ret;
|
||||
|
||||
if (!ai->i_has_dstream)
|
||||
return 0;
|
||||
|
||||
dstream = &ai->i_dstream;
|
||||
ret = apfs_flush_extent_cache(dstream);
|
||||
if (ret) {
|
||||
apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = apfs_dstream_delete_front(sb, dstream->ds_id);
|
||||
if (ret == -ENODATA)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags)
|
||||
#else
|
||||
@@ -1651,8 +1755,6 @@ int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_fi
|
||||
struct apfs_dstream_info *dst_ds = &dst_ai->i_dstream;
|
||||
struct super_block *sb = src_inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
/* TODO: remember to update the maxops in the future */
|
||||
struct apfs_max_ops maxops = {0};
|
||||
const u64 xfield_flags = APFS_INODE_MAINTAIN_DIR_STATS | APFS_INODE_IS_SPARSE | APFS_INODE_HAS_PURGEABLE_FLAGS;
|
||||
int err;
|
||||
|
||||
@@ -1686,7 +1788,7 @@ int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_fi
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
err = apfs_transaction_start(sb, APFS_TRANS_REG);
|
||||
if (err)
|
||||
return err;
|
||||
apfs_inode_join_transaction(sb, src_inode);
|
||||
@@ -1706,8 +1808,10 @@ int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_fi
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
dst_inode->i_mtime = dst_inode->i_ctime = current_time(dst_inode);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
dst_inode->i_mtime = inode_set_ctime_current(dst_inode);
|
||||
#else
|
||||
inode_set_mtime_to_ts(dst_inode, inode_set_ctime_current(dst_inode));
|
||||
#endif
|
||||
dst_inode->i_size = src_inode->i_size;
|
||||
dst_ai->i_key_class = src_ai->i_key_class;
|
||||
@@ -1760,18 +1864,15 @@ fail:
|
||||
static int apfs_extent_create_record(struct super_block *sb, u64 dstream_id, struct apfs_file_extent *extent)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_file_extent_val raw_val;
|
||||
struct apfs_file_extent_key raw_key;
|
||||
int ret = 0;
|
||||
|
||||
apfs_init_file_extent_key(dstream_id, extent->logical_addr, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_file_extent_key(dstream_id, extent->logical_addr, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1800,7 +1901,7 @@ out:
|
||||
* @paddr_end: first block after the extent to put
|
||||
* @paddr_min: don't put references before this block
|
||||
*
|
||||
* Puts a reference to the physical extent range that ends in paddr. Sets
|
||||
* Puts a reference to the physical extent range that ends in @paddr_end. Sets
|
||||
* @paddr_end to the beginning of the extent, so that the caller can continue
|
||||
* with the previous one. Returns 0 on success, or a negative error code in
|
||||
* case of failure.
|
||||
@@ -1835,7 +1936,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1862,7 +1963,7 @@ restart:
|
||||
prev_end = prev_ext.bno + prev_ext.blkcount;
|
||||
if (prev_end < *paddr_end) {
|
||||
/* The extent to put is part of a snapshot */
|
||||
tmp.phys_block_num = MAX(prev_end, paddr_min);
|
||||
tmp.phys_block_num = max(prev_end, paddr_min);
|
||||
tmp.len = (*paddr_end - tmp.phys_block_num) << sb->s_blocksize_bits;
|
||||
ret = apfs_create_update_pext(query, &tmp, -1);
|
||||
*paddr_end = tmp.phys_block_num;
|
||||
@@ -1942,7 +2043,7 @@ static int apfs_range_put_reference(struct super_block *sb, u64 paddr, u64 lengt
|
||||
* @paddr_end: first block after the extent to take
|
||||
* @paddr_min: don't take references before this block
|
||||
*
|
||||
* Takes a reference to the physical extent range that ends in paddr. Sets
|
||||
* Takes a reference to the physical extent range that ends in @paddr_end. Sets
|
||||
* @paddr_end to the beginning of the extent, so that the caller can continue
|
||||
* with the previous one. Returns 0 on success, or a negative error code in
|
||||
* case of failure.
|
||||
@@ -1975,7 +2076,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -2002,7 +2103,7 @@ restart:
|
||||
prev_end = prev_ext.bno + prev_ext.blkcount;
|
||||
if (prev_end < *paddr_end) {
|
||||
/* The extent to take is part of a snapshot */
|
||||
tmp.phys_block_num = MAX(prev_end, paddr_min);
|
||||
tmp.phys_block_num = max(prev_end, paddr_min);
|
||||
tmp.len = (*paddr_end - tmp.phys_block_num) << sb->s_blocksize_bits;
|
||||
ret = apfs_create_update_pext(query, &tmp, +1);
|
||||
*paddr_end = tmp.phys_block_num;
|
||||
@@ -2186,7 +2287,7 @@ int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, si
|
||||
goto out;
|
||||
}
|
||||
|
||||
bhs[idx] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
bhs[idx] = __apfs_getblk(sb, bno);
|
||||
if (!bhs[idx]) {
|
||||
apfs_err(sb, "failed to map block 0x%llx", bno);
|
||||
ret = -EIO;
|
||||
@@ -2197,7 +2298,6 @@ int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, si
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
}
|
||||
@@ -2258,13 +2358,12 @@ void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream)
|
||||
if (ret || bno == 0)
|
||||
return;
|
||||
|
||||
bh = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
bh = __apfs_getblk(sb, bno);
|
||||
if (!bh)
|
||||
return;
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
brelse(bh);
|
||||
|
||||
@@ -5,10 +5,20 @@
|
||||
|
||||
#include "apfs.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||
#include <linux/splice.h>
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
|
||||
typedef int vm_fault_t;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0)
|
||||
static void wait_for_stable_page(struct page *page)
|
||||
{
|
||||
return folio_wait_stable(page_folio(page));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
||||
static vm_fault_t apfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
@@ -18,12 +28,13 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
#endif
|
||||
struct page *page = vmf->page;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct folio *folio;
|
||||
#endif
|
||||
struct inode *inode = file_inode(vma->vm_file);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh, *head;
|
||||
vm_fault_t ret = VM_FAULT_LOCKED;
|
||||
struct apfs_max_ops maxops;
|
||||
int blkcount = PAGE_SIZE >> inode->i_blkbits;
|
||||
unsigned int blocksize, block_start, len;
|
||||
u64 size;
|
||||
int err = 0;
|
||||
@@ -31,12 +42,7 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
sb_start_pagefault(inode->i_sb);
|
||||
file_update_time(vma->vm_file);
|
||||
|
||||
/* Placeholder values, I need to get back to this in the future */
|
||||
maxops.cat = APFS_UPDATE_INODE_MAXOPS() +
|
||||
blkcount * APFS_GET_NEW_BLOCK_MAXOPS();
|
||||
maxops.blks = blkcount;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
err = apfs_transaction_start(sb, APFS_TRANS_REG);
|
||||
if (err)
|
||||
goto out;
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
@@ -54,11 +60,23 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
if (!page_has_buffers(page)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
create_empty_buffers(page, sb->s_blocksize, 0);
|
||||
#else
|
||||
folio = page_folio(page);
|
||||
bh = folio_buffers(folio);
|
||||
if (!bh)
|
||||
bh = create_empty_buffers(folio, sb->s_blocksize, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
size = i_size_read(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)
|
||||
if (page_folio(page)->index == size >> PAGE_SHIFT)
|
||||
#else
|
||||
if (page->index == size >> PAGE_SHIFT)
|
||||
#endif
|
||||
len = size & ~PAGE_MASK;
|
||||
else
|
||||
len = PAGE_SIZE;
|
||||
@@ -99,7 +117,7 @@ out_abort:
|
||||
apfs_transaction_abort(sb);
|
||||
out:
|
||||
if (err)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && !RHEL_VERSION_GE(9, 5)
|
||||
ret = block_page_mkwrite_return(err);
|
||||
#else
|
||||
ret = vmf_fs_error(err);
|
||||
@@ -118,7 +136,7 @@ int apfs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
|
||||
if (!mapping->a_ops->read_folio)
|
||||
#else
|
||||
if (!mapping->a_ops->readpage)
|
||||
@@ -141,6 +159,16 @@ int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
return apfs_sync_fs(sb, true /* wait */);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||
static ssize_t apfs_copy_file_range(struct file *src_file, loff_t src_off,
|
||||
struct file *dst_file, loff_t dst_off,
|
||||
size_t len, unsigned int flags)
|
||||
{
|
||||
return (splice_copy_file_range(src_file, src_off,
|
||||
dst_file, dst_off, len));
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct file_operations apfs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -150,7 +178,9 @@ const struct file_operations apfs_file_operations = {
|
||||
.fsync = apfs_fsync,
|
||||
.unlocked_ioctl = apfs_file_ioctl,
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||
.copy_file_range = apfs_copy_file_range,
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
.copy_file_range = generic_copy_file_range,
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Script to generate the module version header
|
||||
#
|
||||
|
||||
if command -v git >/dev/null 2>&1 && [ -d .git ]; then
|
||||
GIT_COMMIT=$(git describe HEAD | tail -c 9)
|
||||
else
|
||||
GIT_COMMIT="$(grep PACKAGE_VERSION dkms.conf | cut -d '"' -f2)?"
|
||||
fi
|
||||
|
||||
printf '#define GIT_COMMIT\t"%s"\n' "$GIT_COMMIT" > version.h
|
||||
@@ -180,7 +180,7 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
struct apfs_spaceman_free_queue_key *raw_key;
|
||||
|
||||
if (size < sizeof(struct apfs_spaceman_free_queue_key)) {
|
||||
if (size != sizeof(*raw_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
@@ -204,14 +204,17 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
*/
|
||||
int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size < sizeof(struct apfs_omap_key)) {
|
||||
struct apfs_omap_key *raw_key;
|
||||
|
||||
if (size != sizeof(*raw_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
raw_key = raw;
|
||||
|
||||
key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid);
|
||||
key->id = le64_to_cpu(raw_key->ok_oid);
|
||||
key->type = 0;
|
||||
key->number = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_xid);
|
||||
key->number = le64_to_cpu(raw_key->ok_xid);
|
||||
key->name = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include <linux/string.h>
|
||||
#include "libzbitmap.h"
|
||||
|
||||
#define MIN(x, y) ((x) > (y) ? (y) : (x))
|
||||
|
||||
#define ZBM_MAGIC "ZBM\x09"
|
||||
#define ZBM_MAGIC_SZ 4
|
||||
|
||||
|
||||
+25
-94
@@ -1,23 +1,24 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
* Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LZFSE_H
|
||||
#define LZFSE_H
|
||||
@@ -25,71 +26,6 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define __attribute__(X)
|
||||
# pragma warning(disable : 4068)
|
||||
#endif
|
||||
|
||||
#if defined(LZFSE_DLL)
|
||||
# if defined(_WIN32) || defined(__CYGWIN__)
|
||||
# if defined(LZFSE_DLL_EXPORTS)
|
||||
# define LZFSE_API __declspec(dllexport)
|
||||
# else
|
||||
# define LZFSE_API __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(LZFSE_API)
|
||||
# if __GNUC__ >= 4
|
||||
# define LZFSE_API __attribute__((visibility("default")))
|
||||
# else
|
||||
# define LZFSE_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*! @abstract Get the required scratch buffer size to compress using LZFSE. */
|
||||
size_t lzfse_encode_scratch_size(void);
|
||||
|
||||
/*! @abstract Compress a buffer using LZFSE.
|
||||
*
|
||||
* @param dst_buffer
|
||||
* Pointer to the first byte of the destination buffer.
|
||||
*
|
||||
* @param dst_size
|
||||
* Size of the destination buffer in bytes.
|
||||
*
|
||||
* @param src_buffer
|
||||
* Pointer to the first byte of the source buffer.
|
||||
*
|
||||
* @param src_size
|
||||
* Size of the source buffer in bytes.
|
||||
*
|
||||
* @param scratch_buffer
|
||||
* If non-NULL, a pointer to scratch space for the routine to use as workspace;
|
||||
* the routine may use up to lzfse_encode_scratch_size( ) bytes of workspace
|
||||
* during its operation, and will not perform any internal allocations. If
|
||||
* NULL, the routine may allocate its own memory to use during operation via
|
||||
* a single call to malloc( ), and will release it by calling free( ) prior
|
||||
* to returning. For most use, passing NULL is perfectly satisfactory, but if
|
||||
* you require strict control over allocation, you will want to pass an
|
||||
* explicit scratch buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written to the destination buffer if the input is
|
||||
* successfully compressed. If the input cannot be compressed to fit into
|
||||
* the provided buffer, or an error occurs, zero is returned, and the
|
||||
* contents of dst_buffer are unspecified. */
|
||||
size_t lzfse_encode_buffer(uint8_t *__restrict dst_buffer,
|
||||
size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer,
|
||||
size_t src_size,
|
||||
void *__restrict scratch_buffer);
|
||||
|
||||
/*! @abstract Get the required scratch buffer size to decompress using LZFSE. */
|
||||
size_t lzfse_decode_scratch_size(void);
|
||||
|
||||
@@ -122,15 +58,10 @@ size_t lzfse_decode_scratch_size(void);
|
||||
* successfully decompressed. If there is not enough space in the destination
|
||||
* buffer to hold the entire expanded output, only the first dst_size bytes
|
||||
* will be written to the buffer and dst_size is returned. Note that this
|
||||
* behavior differs from that of lzfse_encode_buffer. */
|
||||
size_t lzfse_decode_buffer(uint8_t *__restrict dst_buffer,
|
||||
size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer,
|
||||
size_t src_size,
|
||||
void *__restrict scratch_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
* behavior differs from that of lzfse_encode_buffer.
|
||||
*/
|
||||
size_t lzfse_decode_buffer(uint8_t *__restrict dst_buffer, size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer, size_t src_size,
|
||||
void *__restrict scratch_buffer);
|
||||
|
||||
#endif /* LZFSE_H */
|
||||
+65
-59
@@ -1,74 +1,80 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// LZFSE decode API
|
||||
/* LZFSE decode API */
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "lzfse.h"
|
||||
#include "lzfse_internal.h"
|
||||
|
||||
size_t lzfse_decode_scratch_size(void) { return sizeof(lzfse_decoder_state); }
|
||||
size_t lzfse_decode_scratch_size(void)
|
||||
{
|
||||
return sizeof(lzfse_decoder_state);
|
||||
}
|
||||
|
||||
static size_t lzfse_decode_buffer_with_scratch(uint8_t *__restrict dst_buffer,
|
||||
size_t dst_size, const uint8_t *__restrict src_buffer,
|
||||
size_t src_size, void *__restrict scratch_buffer) {
|
||||
int status;
|
||||
lzfse_decoder_state *s = (lzfse_decoder_state *)scratch_buffer;
|
||||
memset(s, 0x00, sizeof(*s));
|
||||
static size_t lzfse_decode_buffer_with_scratch(uint8_t *__restrict dst_buffer, size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer,
|
||||
size_t src_size, void *__restrict scratch_buffer)
|
||||
{
|
||||
int status;
|
||||
lzfse_decoder_state *s = (lzfse_decoder_state *)scratch_buffer;
|
||||
|
||||
// Initialize state
|
||||
s->src = src_buffer;
|
||||
s->src_begin = src_buffer;
|
||||
s->src_end = s->src + src_size;
|
||||
s->dst = dst_buffer;
|
||||
s->dst_begin = dst_buffer;
|
||||
s->dst_end = dst_buffer + dst_size;
|
||||
memset(s, 0x00, sizeof(*s));
|
||||
|
||||
// Decode
|
||||
status = lzfse_decode(s);
|
||||
if (status == LZFSE_STATUS_DST_FULL)
|
||||
return dst_size;
|
||||
if (status != LZFSE_STATUS_OK)
|
||||
return 0; // failed
|
||||
return (size_t)(s->dst - dst_buffer); // bytes written
|
||||
/* Initialize state */
|
||||
s->src = src_buffer;
|
||||
s->src_begin = src_buffer;
|
||||
s->src_end = s->src + src_size;
|
||||
s->dst = dst_buffer;
|
||||
s->dst_begin = dst_buffer;
|
||||
s->dst_end = dst_buffer + dst_size;
|
||||
|
||||
/* Decode */
|
||||
status = lzfse_decode(s);
|
||||
if (status == LZFSE_STATUS_DST_FULL)
|
||||
return dst_size;
|
||||
if (status != LZFSE_STATUS_OK)
|
||||
return 0; /* failed */
|
||||
return (size_t)(s->dst - dst_buffer); /* bytes written */
|
||||
}
|
||||
|
||||
size_t lzfse_decode_buffer(uint8_t *__restrict dst_buffer, size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer,
|
||||
size_t src_size, void *__restrict scratch_buffer) {
|
||||
int has_malloc = 0;
|
||||
size_t ret = 0;
|
||||
const uint8_t *__restrict src_buffer, size_t src_size,
|
||||
void *__restrict scratch_buffer)
|
||||
{
|
||||
int has_malloc = 0;
|
||||
size_t ret = 0;
|
||||
|
||||
// Deal with the possible NULL pointer
|
||||
if (scratch_buffer == NULL) {
|
||||
// +1 in case scratch size could be zero
|
||||
scratch_buffer = kmalloc(lzfse_decode_scratch_size() + 1, GFP_KERNEL);
|
||||
has_malloc = 1;
|
||||
}
|
||||
if (scratch_buffer == NULL)
|
||||
return 0;
|
||||
ret = lzfse_decode_buffer_with_scratch(dst_buffer,
|
||||
dst_size, src_buffer,
|
||||
src_size, scratch_buffer);
|
||||
if (has_malloc)
|
||||
kfree(scratch_buffer);
|
||||
return ret;
|
||||
/* Deal with the possible NULL pointer */
|
||||
if (scratch_buffer == NULL) {
|
||||
/* +1 in case scratch size could be zero */
|
||||
scratch_buffer = kmalloc(lzfse_decode_scratch_size() + 1, GFP_KERNEL);
|
||||
has_malloc = 1;
|
||||
}
|
||||
if (scratch_buffer == NULL)
|
||||
return 0;
|
||||
ret = lzfse_decode_buffer_with_scratch(dst_buffer, dst_size, src_buffer, src_size,
|
||||
scratch_buffer);
|
||||
if (has_malloc)
|
||||
kfree(scratch_buffer);
|
||||
return ret;
|
||||
}
|
||||
+647
-556
File diff suppressed because it is too large.
Load diff
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// LZFSE encode API
|
||||
|
||||
#include "lzfse.h"
|
||||
#include "lzfse_internal.h"
|
||||
|
||||
size_t lzfse_encode_scratch_size() {
|
||||
size_t s1 = sizeof(lzfse_encoder_state);
|
||||
size_t s2 = lzvn_encode_scratch_size();
|
||||
return (s1 > s2) ? s1 : s2; // max(lzfse,lzvn)
|
||||
}
|
||||
|
||||
size_t lzfse_encode_buffer_with_scratch(uint8_t *__restrict dst_buffer,
|
||||
size_t dst_size, const uint8_t *__restrict src_buffer,
|
||||
size_t src_size, void *__restrict scratch_buffer) {
|
||||
const size_t original_size = src_size;
|
||||
|
||||
// If input is really really small, go directly to uncompressed buffer
|
||||
// (because LZVN will refuse to encode it, and we will report a failure)
|
||||
if (src_size < LZVN_ENCODE_MIN_SRC_SIZE)
|
||||
goto try_uncompressed;
|
||||
|
||||
// If input is too small, try encoding with LZVN
|
||||
if (src_size < LZFSE_ENCODE_LZVN_THRESHOLD) {
|
||||
// need header + end-of-stream marker
|
||||
size_t extra_size = 4 + sizeof(lzvn_compressed_block_header);
|
||||
if (dst_size <= extra_size)
|
||||
goto try_uncompressed; // DST is really too small, give up
|
||||
|
||||
size_t sz = lzvn_encode_buffer(
|
||||
dst_buffer + sizeof(lzvn_compressed_block_header),
|
||||
dst_size - extra_size, src_buffer, src_size, scratch_buffer);
|
||||
if (sz == 0 || sz >= src_size)
|
||||
goto try_uncompressed; // failed, or no compression, fall back to
|
||||
// uncompressed block
|
||||
|
||||
// If we could encode, setup header and end-of-stream marker (we left room
|
||||
// for them, no need to test)
|
||||
lzvn_compressed_block_header header;
|
||||
header.magic = LZFSE_COMPRESSEDLZVN_BLOCK_MAGIC;
|
||||
header.n_raw_bytes = (uint32_t)src_size;
|
||||
header.n_payload_bytes = (uint32_t)sz;
|
||||
memcpy(dst_buffer, &header, sizeof(header));
|
||||
store4(dst_buffer + sizeof(lzvn_compressed_block_header) + sz,
|
||||
LZFSE_ENDOFSTREAM_BLOCK_MAGIC);
|
||||
|
||||
return sz + extra_size;
|
||||
}
|
||||
|
||||
// Try encoding with LZFSE
|
||||
{
|
||||
lzfse_encoder_state *state = scratch_buffer;
|
||||
memset(state, 0x00, sizeof *state);
|
||||
if (lzfse_encode_init(state) != LZFSE_STATUS_OK)
|
||||
goto try_uncompressed;
|
||||
state->dst = dst_buffer;
|
||||
state->dst_begin = dst_buffer;
|
||||
state->dst_end = &dst_buffer[dst_size];
|
||||
state->src = src_buffer;
|
||||
state->src_encode_i = 0;
|
||||
|
||||
if (src_size >= 0xffffffffU) {
|
||||
// lzfse only uses 32 bits for offsets internally, so if the input
|
||||
// buffer is really huge, we need to process it in smaller chunks.
|
||||
// Note that we switch over to this path for sizes much smaller
|
||||
// 2GB because it's actually faster to change algorithms well before
|
||||
// it's necessary for correctness.
|
||||
// The first chunk, we just process normally.
|
||||
const lzfse_offset encoder_block_size = 262144;
|
||||
state->src_end = encoder_block_size;
|
||||
if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
|
||||
goto try_uncompressed;
|
||||
src_size -= encoder_block_size;
|
||||
while (src_size >= encoder_block_size) {
|
||||
// All subsequent chunks require a translation to keep the offsets
|
||||
// from getting too big. Note that we are always going from
|
||||
// encoder_block_size up to 2*encoder_block_size so that the
|
||||
// offsets remain positive (as opposed to resetting to zero and
|
||||
// having negative offsets).
|
||||
state->src_end = 2 * encoder_block_size;
|
||||
if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
|
||||
goto try_uncompressed;
|
||||
lzfse_encode_translate(state, encoder_block_size);
|
||||
src_size -= encoder_block_size;
|
||||
}
|
||||
// Set the end for the final chunk.
|
||||
state->src_end = encoder_block_size + (lzfse_offset)src_size;
|
||||
}
|
||||
// If the source buffer is small enough to use 32-bit offsets, we simply
|
||||
// encode the whole thing in a single chunk.
|
||||
else
|
||||
state->src_end = (lzfse_offset)src_size;
|
||||
// This is either the trailing chunk (if the source file is huge), or
|
||||
// the whole source file.
|
||||
if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
|
||||
goto try_uncompressed;
|
||||
if (lzfse_encode_finish(state) != LZFSE_STATUS_OK)
|
||||
goto try_uncompressed;
|
||||
// No error occured, return compressed size.
|
||||
return state->dst - dst_buffer;
|
||||
}
|
||||
|
||||
try_uncompressed:
|
||||
// Compression failed for some reason. If we can fit the data into the
|
||||
// output buffer uncompressed, go ahead and do that instead.
|
||||
if (original_size + 12 <= dst_size && original_size < INT32_MAX) {
|
||||
uncompressed_block_header header = {.magic = LZFSE_UNCOMPRESSED_BLOCK_MAGIC,
|
||||
.n_raw_bytes = (uint32_t)src_size};
|
||||
uint8_t *dst_end = dst_buffer;
|
||||
memcpy(dst_end, &header, sizeof header);
|
||||
dst_end += sizeof header;
|
||||
memcpy(dst_end, src_buffer, original_size);
|
||||
dst_end += original_size;
|
||||
store4(dst_end, LZFSE_ENDOFSTREAM_BLOCK_MAGIC);
|
||||
dst_end += 4;
|
||||
return dst_end - dst_buffer;
|
||||
}
|
||||
|
||||
// Otherwise, there's nothing we can do, so return zero.
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t lzfse_encode_buffer(uint8_t *__restrict dst_buffer, size_t dst_size,
|
||||
const uint8_t *__restrict src_buffer,
|
||||
size_t src_size, void *__restrict scratch_buffer) {
|
||||
int has_malloc = 0;
|
||||
size_t ret = 0;
|
||||
|
||||
// Deal with the possible NULL pointer
|
||||
if (scratch_buffer == NULL) {
|
||||
// +1 in case scratch size could be zero
|
||||
scratch_buffer = malloc(lzfse_encode_scratch_size() + 1);
|
||||
has_malloc = 1;
|
||||
}
|
||||
if (scratch_buffer == NULL)
|
||||
return 0;
|
||||
ret = lzfse_encode_buffer_with_scratch(dst_buffer,
|
||||
dst_size, src_buffer,
|
||||
src_size, scratch_buffer);
|
||||
if (has_malloc)
|
||||
free(scratch_buffer);
|
||||
return ret;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 20 of 39 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user