You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Migrate all compression code to Rust
This commit is contained in:
@@ -30,8 +30,11 @@ der = { workspace = true, features = ["derive", "pem"] }
|
||||
fdt = { workspace = true }
|
||||
bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
||||
num-traits = { workspace = true }
|
||||
libz-rs-sys = { workspace = true }
|
||||
libbz2-rs-sys = { workspace = true }
|
||||
flate2 = { workspace = true, features = ["zlib-rs"] }
|
||||
bzip2 = { workspace = true, features = ["libbz2-rs-sys"] }
|
||||
lz4 = { workspace = true }
|
||||
xz2 = { workspace = true }
|
||||
zopfli = { workspace = true, features = ["gzip"] }
|
||||
|
||||
# Pin version to prevent cargo update break builds
|
||||
block-buffer = { workspace = true }
|
||||
|
||||
+37
-41
@@ -8,7 +8,6 @@
|
||||
#include "boot-rs.hpp"
|
||||
#include "bootimg.hpp"
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -16,17 +15,13 @@ using namespace std;
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
#define SHA_DIGEST_SIZE 20
|
||||
|
||||
static void decompress(format_t type, int fd, const void *in, size_t size) {
|
||||
auto ptr = get_decoder(type, make_unique<fd_stream>(fd));
|
||||
ptr->write(in, size);
|
||||
static void decompress(FileFormat type, int fd, const void *in, size_t size) {
|
||||
decompress_bytes(type, byte_view { in, size }, fd);
|
||||
}
|
||||
|
||||
static off_t compress(format_t type, int fd, const void *in, size_t size) {
|
||||
static off_t compress_len(FileFormat type, byte_view in, int fd) {
|
||||
auto prev = lseek(fd, 0, SEEK_CUR);
|
||||
{
|
||||
auto strm = get_encoder(type, make_unique<fd_stream>(fd));
|
||||
strm->write(in, size);
|
||||
}
|
||||
compress_bytes(type, in, fd);
|
||||
auto now = lseek(fd, 0, SEEK_CUR);
|
||||
return now - prev;
|
||||
}
|
||||
@@ -149,29 +144,30 @@ void dyn_img_hdr::load_hdr_file() {
|
||||
});
|
||||
}
|
||||
|
||||
boot_img::boot_img(const char *image) : map(image) {
|
||||
boot_img::boot_img(const char *image) :
|
||||
map(image), k_fmt(FileFormat::UNKNOWN), r_fmt(FileFormat::UNKNOWN), e_fmt(FileFormat::UNKNOWN) {
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
||||
for (const uint8_t *addr = map.buf(); addr < map.buf() + map.sz(); ++addr) {
|
||||
format_t fmt = check_fmt(addr, map.sz());
|
||||
FileFormat fmt = check_fmt(addr, map.sz());
|
||||
switch (fmt) {
|
||||
case CHROMEOS:
|
||||
case FileFormat::CHROMEOS:
|
||||
// chromeos require external signing
|
||||
flags[CHROMEOS_FLAG] = true;
|
||||
addr += 65535;
|
||||
break;
|
||||
case DHTB:
|
||||
case FileFormat::DHTB:
|
||||
flags[DHTB_FLAG] = true;
|
||||
flags[SEANDROID_FLAG] = true;
|
||||
fprintf(stderr, "DHTB_HDR\n");
|
||||
addr += sizeof(dhtb_hdr) - 1;
|
||||
break;
|
||||
case BLOB:
|
||||
case FileFormat::BLOB:
|
||||
flags[BLOB_FLAG] = true;
|
||||
fprintf(stderr, "TEGRA_BLOB\n");
|
||||
addr += sizeof(blob_hdr) - 1;
|
||||
break;
|
||||
case AOSP:
|
||||
case AOSP_VENDOR:
|
||||
case FileFormat::AOSP:
|
||||
case FileFormat::AOSP_VENDOR:
|
||||
if (parse_image(addr, fmt))
|
||||
return;
|
||||
// fallthrough
|
||||
@@ -257,9 +253,9 @@ static int find_dtb_offset(const uint8_t *buf, unsigned sz) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static format_t check_fmt_lg(const uint8_t *buf, unsigned sz) {
|
||||
format_t fmt = check_fmt(buf, sz);
|
||||
if (fmt == LZ4_LEGACY) {
|
||||
static FileFormat check_fmt_lg(const uint8_t *buf, unsigned sz) {
|
||||
FileFormat fmt = check_fmt(buf, sz);
|
||||
if (fmt == FileFormat::LZ4_LEGACY) {
|
||||
// We need to check if it is LZ4_LG
|
||||
uint32_t off = 4;
|
||||
uint32_t block_sz;
|
||||
@@ -267,7 +263,7 @@ static format_t check_fmt_lg(const uint8_t *buf, unsigned sz) {
|
||||
memcpy(&block_sz, buf + off, sizeof(block_sz));
|
||||
off += sizeof(block_sz);
|
||||
if (off + block_sz > sz)
|
||||
return LZ4_LG;
|
||||
return FileFormat::LZ4_LG;
|
||||
off += block_sz;
|
||||
}
|
||||
}
|
||||
@@ -276,8 +272,8 @@ static format_t check_fmt_lg(const uint8_t *buf, unsigned sz) {
|
||||
|
||||
#define CMD_MATCH(s) BUFFER_MATCH(h->cmdline, s)
|
||||
|
||||
pair<const uint8_t *, dyn_img_hdr *> boot_img::create_hdr(const uint8_t *addr, format_t type) {
|
||||
if (type == AOSP_VENDOR) {
|
||||
pair<const uint8_t *, dyn_img_hdr *> boot_img::create_hdr(const uint8_t *addr, FileFormat type) {
|
||||
if (type == FileFormat::AOSP_VENDOR) {
|
||||
fprintf(stderr, "VENDOR_BOOT_HDR\n");
|
||||
auto h = reinterpret_cast<const boot_img_hdr_vnd_v3*>(addr);
|
||||
switch (h->header_version) {
|
||||
@@ -377,7 +373,7 @@ off += hdr->name##_size(); \
|
||||
off = align_to(off, hdr->page_size()); \
|
||||
assert_off();
|
||||
|
||||
bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
auto [base_addr, hdr] = create_hdr(p, type);
|
||||
if (hdr == nullptr) {
|
||||
fprintf(stderr, "Invalid boot image header!\n");
|
||||
@@ -419,7 +415,7 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
}
|
||||
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
if (k_fmt == MTK) {
|
||||
if (k_fmt == FileFormat::MTK) {
|
||||
fprintf(stderr, "MTK_KERNEL_HDR\n");
|
||||
flags[MTK_KERNEL] = true;
|
||||
k_hdr = reinterpret_cast<const mtk_hdr *>(kernel);
|
||||
@@ -429,14 +425,14 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
hdr->kernel_size() -= sizeof(mtk_hdr);
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
}
|
||||
if (k_fmt == ZIMAGE) {
|
||||
if (k_fmt == FileFormat::ZIMAGE) {
|
||||
z_hdr = reinterpret_cast<const zimage_hdr *>(kernel);
|
||||
|
||||
const uint8_t* found_pos = 0;
|
||||
|
||||
|
||||
for (const uint8_t* search_pos = kernel + 0x28; search_pos < kernel + hdr->kernel_size(); search_pos++) {
|
||||
// ^^^^^^ +0x28 to search after zimage header and magic
|
||||
if (check_fmt_lg(search_pos, hdr->kernel_size() - (search_pos - kernel)) != UNKNOWN) {
|
||||
if (check_fmt_lg(search_pos, hdr->kernel_size() - (search_pos - kernel)) != FileFormat::UNKNOWN) {
|
||||
found_pos = search_pos;
|
||||
search_pos = kernel + hdr->kernel_size();
|
||||
}
|
||||
@@ -488,7 +484,7 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
reinterpret_cast<table_entry *>(vendor_ramdisk_table),
|
||||
hdr->vendor_ramdisk_table_entry_num());
|
||||
for (auto &it : table) {
|
||||
format_t fmt = check_fmt_lg(ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
FileFormat fmt = check_fmt_lg(ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
fprintf(stderr,
|
||||
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
|
||||
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
|
||||
@@ -496,7 +492,7 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
}
|
||||
} else {
|
||||
r_fmt = check_fmt_lg(ramdisk, size);
|
||||
if (r_fmt == MTK) {
|
||||
if (r_fmt == FileFormat::MTK) {
|
||||
fprintf(stderr, "MTK_RAMDISK_HDR\n");
|
||||
flags[MTK_RAMDISK] = true;
|
||||
r_hdr = reinterpret_cast<const mtk_hdr *>(ramdisk);
|
||||
@@ -555,8 +551,8 @@ bool boot_img::verify(const char *cert) const {
|
||||
int split_image_dtb(const char *filename, bool skip_decomp) {
|
||||
mmap_data img(filename);
|
||||
|
||||
if (int off = find_dtb_offset(img.buf(), img.sz()); off > 0) {
|
||||
format_t fmt = check_fmt_lg(img.buf(), img.sz());
|
||||
if (size_t off = find_dtb_offset(img.buf(), img.sz()); off > 0) {
|
||||
FileFormat fmt = check_fmt_lg(img.buf(), img.sz());
|
||||
if (!skip_decomp && COMPRESSED(fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(fmt, fd, img.buf(), off);
|
||||
@@ -609,7 +605,7 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
ssprintf(file_name, sizeof(file_name), "%s.cpio", it.ramdisk_name);
|
||||
}
|
||||
owned_fd fd = xopenat(dirfd, file_name, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644);
|
||||
format_t fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
if (!skip_decomp && COMPRESSED(fmt)) {
|
||||
decompress(fmt, fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
} else {
|
||||
@@ -719,8 +715,8 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
mmap_data m(KERNEL_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.k_fmt)) {
|
||||
// Always use zopfli for zImage compression
|
||||
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == GZIP) ? ZOPFLI : boot.k_fmt;
|
||||
hdr->kernel_size() = compress(fmt, fd, m.buf(), m.sz());
|
||||
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == FileFormat::GZIP) ? FileFormat::ZOPFLI : boot.k_fmt;
|
||||
hdr->kernel_size() = compress_len(fmt, m, fd);
|
||||
} else {
|
||||
hdr->kernel_size() = xwrite(fd, m.buf(), m.sz());
|
||||
}
|
||||
@@ -783,10 +779,10 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
ssprintf(file_name, sizeof(file_name), "%s.cpio", it.ramdisk_name);
|
||||
}
|
||||
mmap_data m(dirfd, file_name);
|
||||
format_t fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
it.ramdisk_offset = ramdisk_offset;
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(fmt)) {
|
||||
it.ramdisk_size = compress(fmt, fd, m.buf(), m.sz());
|
||||
it.ramdisk_size = compress_len(fmt, m, fd);
|
||||
} else {
|
||||
it.ramdisk_size = xwrite(fd, m.buf(), m.sz());
|
||||
}
|
||||
@@ -798,15 +794,15 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
} else if (access(RAMDISK_FILE, R_OK) == 0) {
|
||||
mmap_data m(RAMDISK_FILE);
|
||||
auto r_fmt = boot.r_fmt;
|
||||
if (!skip_comp && !hdr->is_vendor() && hdr->header_version() == 4 && r_fmt != LZ4_LEGACY) {
|
||||
if (!skip_comp && !hdr->is_vendor() && hdr->header_version() == 4 && r_fmt != FileFormat::LZ4_LEGACY) {
|
||||
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
|
||||
// and they have to use the exact same compression method. v4 GKIs are required to
|
||||
// use lz4 (legacy), so hardcode the format here.
|
||||
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[LZ4_LEGACY]);
|
||||
r_fmt = LZ4_LEGACY;
|
||||
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[FileFormat::LZ4_LEGACY]);
|
||||
r_fmt = FileFormat::LZ4_LEGACY;
|
||||
}
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
|
||||
hdr->ramdisk_size() = compress(r_fmt, fd, m.buf(), m.sz());
|
||||
hdr->ramdisk_size() = compress_len(r_fmt, m, fd);
|
||||
} else {
|
||||
hdr->ramdisk_size() = xwrite(fd, m.buf(), m.sz());
|
||||
}
|
||||
@@ -825,7 +821,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
if (access(EXTRA_FILE, R_OK) == 0) {
|
||||
mmap_data m(EXTRA_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.e_fmt)) {
|
||||
hdr->extra_size() = compress(boot.e_fmt, fd, m.buf(), m.sz());
|
||||
hdr->extra_size() = compress_len(boot.e_fmt, m, fd);
|
||||
} else {
|
||||
hdr->extra_size() = xwrite(fd, m.buf(), m.sz());
|
||||
}
|
||||
|
||||
@@ -610,9 +610,9 @@ struct boot_img {
|
||||
std::bitset<BOOT_FLAGS_MAX> flags;
|
||||
|
||||
// The format of kernel, ramdisk and extra
|
||||
format_t k_fmt = UNKNOWN;
|
||||
format_t r_fmt = UNKNOWN;
|
||||
format_t e_fmt = UNKNOWN;
|
||||
FileFormat k_fmt;
|
||||
FileFormat r_fmt;
|
||||
FileFormat e_fmt;
|
||||
|
||||
/*************************************************************
|
||||
* Following pointers points within the read-only mmap region
|
||||
@@ -672,8 +672,8 @@ struct boot_img {
|
||||
boot_img(const char *);
|
||||
~boot_img();
|
||||
|
||||
bool parse_image(const uint8_t *addr, format_t type);
|
||||
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, format_t type);
|
||||
bool parse_image(const uint8_t *addr, FileFormat type);
|
||||
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
|
||||
|
||||
// Rust FFI
|
||||
rust::Slice<const uint8_t> get_payload() const { return payload; }
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Public header file for the library. ---*/
|
||||
/*--- bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.1.0 of 6 September 2010
|
||||
Copyright (C) 1996-2010 Julian Seward <jseward@acm.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#ifndef _BZLIB_H
|
||||
#define _BZLIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BZ_RUN 0
|
||||
#define BZ_FLUSH 1
|
||||
#define BZ_FINISH 2
|
||||
|
||||
#define BZ_OK 0
|
||||
#define BZ_RUN_OK 1
|
||||
#define BZ_FLUSH_OK 2
|
||||
#define BZ_FINISH_OK 3
|
||||
#define BZ_STREAM_END 4
|
||||
#define BZ_SEQUENCE_ERROR (-1)
|
||||
#define BZ_PARAM_ERROR (-2)
|
||||
#define BZ_MEM_ERROR (-3)
|
||||
#define BZ_DATA_ERROR (-4)
|
||||
#define BZ_DATA_ERROR_MAGIC (-5)
|
||||
#define BZ_IO_ERROR (-6)
|
||||
#define BZ_UNEXPECTED_EOF (-7)
|
||||
#define BZ_OUTBUFF_FULL (-8)
|
||||
#define BZ_CONFIG_ERROR (-9)
|
||||
|
||||
typedef
|
||||
struct {
|
||||
char *next_in;
|
||||
unsigned int avail_in;
|
||||
unsigned int total_in_lo32;
|
||||
unsigned int total_in_hi32;
|
||||
|
||||
char *next_out;
|
||||
unsigned int avail_out;
|
||||
unsigned int total_out_lo32;
|
||||
unsigned int total_out_hi32;
|
||||
|
||||
void *state;
|
||||
|
||||
void *(*bzalloc)(void *,int,int);
|
||||
void (*bzfree)(void *,void *);
|
||||
void *opaque;
|
||||
}
|
||||
bz_stream;
|
||||
|
||||
|
||||
#ifndef BZ_IMPORT
|
||||
#define BZ_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
/* Need a definitition for FILE */
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# ifdef small
|
||||
/* windows.h define small to char */
|
||||
# undef small
|
||||
# endif
|
||||
# ifndef WINAPI
|
||||
# define WINAPI
|
||||
# endif
|
||||
# ifdef BZ_EXPORT
|
||||
# define BZ_API(func) WINAPI func
|
||||
# define BZ_EXTERN extern
|
||||
# else
|
||||
/* import windows dll dynamically */
|
||||
# define BZ_API(func) (WINAPI * func)
|
||||
# define BZ_EXTERN
|
||||
# endif
|
||||
#else
|
||||
# define BZ_API(func) func
|
||||
#endif
|
||||
|
||||
#ifndef BZ_EXTERN
|
||||
#define BZ_EXTERN extern
|
||||
#endif
|
||||
|
||||
/*-- Core (low-level) library functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
|
||||
bz_stream* strm,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompress) (
|
||||
bz_stream* strm,
|
||||
int action
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
|
||||
bz_stream *strm,
|
||||
int verbosity,
|
||||
int small
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
|
||||
bz_stream *strm
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*-- High(er) level library functions --*/
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
#define BZ_MAX_UNUSED 5000
|
||||
|
||||
typedef void BZFILE;
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int verbosity,
|
||||
int small,
|
||||
void* unused,
|
||||
int nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void** unused,
|
||||
int* nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzRead) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWrite) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in,
|
||||
unsigned int* nbytes_out
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in_lo32,
|
||||
unsigned int* nbytes_in_hi32,
|
||||
unsigned int* nbytes_out_lo32,
|
||||
unsigned int* nbytes_out_hi32
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/*-- Utility functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int small,
|
||||
int verbosity
|
||||
);
|
||||
|
||||
|
||||
/*--
|
||||
Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
|
||||
to support better zlib compatibility.
|
||||
This code is not _officially_ part of libbzip2 (yet);
|
||||
I haven't tested it, documented it, or considered the
|
||||
threading-safeness of it.
|
||||
If this code breaks, please contact both Yoshioka and me.
|
||||
--*/
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
|
||||
void
|
||||
);
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
|
||||
const char *path,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
|
||||
int fd,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzread) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzwrite) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzflush) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzclose) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
|
||||
BZFILE *b,
|
||||
int *errnum
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,762 +0,0 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <zlib.h>
|
||||
#include "bzlib.h"
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
#include <lz4hc.h>
|
||||
#include <zopfli/util.h>
|
||||
#include <zopfli/deflate.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define bwrite this->base->write
|
||||
|
||||
constexpr size_t CHUNK = 0x40000;
|
||||
constexpr size_t LZ4_UNCOMPRESSED = 0x800000;
|
||||
constexpr size_t LZ4_COMPRESSED = LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED);
|
||||
|
||||
class gz_strm : public filter_out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
~gz_strm() override {
|
||||
do_write(nullptr, 0, Z_FINISH);
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
inflateEnd(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
deflateEnd(&strm);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE,
|
||||
WAIT,
|
||||
COPY
|
||||
} mode;
|
||||
|
||||
gz_strm(mode_t mode, out_strm_ptr &&base) :
|
||||
filter_out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
inflateInit2(&strm, 15 | 16);
|
||||
break;
|
||||
case ENCODE:
|
||||
deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
z_stream strm;
|
||||
uint8_t outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, int flush) {
|
||||
if (mode == WAIT) {
|
||||
if (len == 0) return true;
|
||||
Bytef b[1] = {0x1f};
|
||||
if (*(Bytef *)buf == 0x8b) {
|
||||
mode = DECODE;
|
||||
inflateReset(&strm);
|
||||
strm.next_in = b;
|
||||
strm.avail_in = 1;
|
||||
inflate(&strm, flush);
|
||||
} else {
|
||||
mode = COPY;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
strm.next_in = (Bytef *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
int code;
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = inflate(&strm, flush);
|
||||
break;
|
||||
case ENCODE:
|
||||
code = deflate(&strm, flush);
|
||||
break;
|
||||
case COPY:
|
||||
return true;
|
||||
default:
|
||||
// should have been handled
|
||||
return false;
|
||||
}
|
||||
if (code == Z_STREAM_ERROR) {
|
||||
LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
if (mode == DECODE && code == Z_STREAM_END) {
|
||||
if (strm.avail_in > 1) {
|
||||
if (strm.next_in[0] == 0x1f && strm.next_in[1] == 0x8b) {
|
||||
// There is still data in the stream, we need to reset the stream
|
||||
// and continue decoding
|
||||
inflateReset(&strm);
|
||||
strm.avail_out = 0;
|
||||
continue;
|
||||
}
|
||||
} else if (strm.avail_in == 1) {
|
||||
if (strm.next_in[0] == 0x1f) {
|
||||
// If there is only one byte left, we need to wait for the next byte
|
||||
// to determine if it is a gzip header
|
||||
mode = WAIT;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// The next inflate won't consume any data but fallback
|
||||
// to the previous two conditions
|
||||
return true;
|
||||
}
|
||||
// There is still data in the stream, we need to copy it
|
||||
mode = COPY;
|
||||
return true;
|
||||
}
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class gz_decoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_decoder(out_strm_ptr &&base) : gz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class gz_encoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_encoder(out_strm_ptr &&base) : gz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class zopfli_encoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit zopfli_encoder(out_strm_ptr &&base) :
|
||||
chunk_out_stream(std::move(base), ZOPFLI_MASTER_BLOCK_SIZE),
|
||||
zo{}, out(nullptr), outsize(0), crc(crc32(0L, Z_NULL, 0)), in_total(0), bp(0) {
|
||||
ZopfliInitOptions(&zo);
|
||||
|
||||
// This config is already better than gzip -9
|
||||
zo.numiterations = 1;
|
||||
zo.blocksplitting = 0;
|
||||
|
||||
ZOPFLI_APPEND_DATA(31, &out, &outsize); /* ID1 */
|
||||
ZOPFLI_APPEND_DATA(139, &out, &outsize); /* ID2 */
|
||||
ZOPFLI_APPEND_DATA(8, &out, &outsize); /* CM */
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize); /* FLG */
|
||||
/* MTIME */
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA(0, &out, &outsize);
|
||||
|
||||
ZOPFLI_APPEND_DATA(2, &out, &outsize); /* XFL, 2 indicates best compression. */
|
||||
ZOPFLI_APPEND_DATA(3, &out, &outsize); /* OS follows Unix conventions. */
|
||||
}
|
||||
|
||||
~zopfli_encoder() override {
|
||||
finalize();
|
||||
|
||||
/* CRC */
|
||||
ZOPFLI_APPEND_DATA(crc % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 8) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 16) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((crc >> 24) % 256, &out, &outsize);
|
||||
|
||||
/* ISIZE */
|
||||
ZOPFLI_APPEND_DATA(in_total % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 8) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 16) % 256, &out, &outsize);
|
||||
ZOPFLI_APPEND_DATA((in_total >> 24) % 256, &out, &outsize);
|
||||
|
||||
bwrite(out, outsize);
|
||||
free(out);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool final) override {
|
||||
auto in = static_cast<const unsigned char *>(buf);
|
||||
|
||||
in_total += len;
|
||||
crc = crc32(crc, in, len);
|
||||
|
||||
ZopfliDeflatePart(&zo, 2, final, in, 0, len, &bp, &out, &outsize);
|
||||
|
||||
// ZOPFLI_APPEND_DATA is extremely dumb, so we always preserve the
|
||||
// last byte to make sure that realloc is used instead of malloc
|
||||
if (!bwrite(out, outsize - 1))
|
||||
return false;
|
||||
out[0] = out[outsize - 1];
|
||||
outsize = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ZopfliOptions zo;
|
||||
unsigned char *out;
|
||||
size_t outsize;
|
||||
unsigned long crc;
|
||||
uint32_t in_total;
|
||||
unsigned char bp;
|
||||
};
|
||||
|
||||
class bz_strm : public filter_out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, BZ_RUN);
|
||||
}
|
||||
|
||||
~bz_strm() override {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
BZ2_bzDecompressEnd(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
do_write(nullptr, 0, BZ_FINISH);
|
||||
BZ2_bzCompressEnd(&strm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE
|
||||
} mode;
|
||||
|
||||
bz_strm(mode_t mode, out_strm_ptr &&base) :
|
||||
filter_out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
BZ2_bzDecompressInit(&strm, 0, 0);
|
||||
break;
|
||||
case ENCODE:
|
||||
BZ2_bzCompressInit(&strm, 9, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bz_stream strm;
|
||||
char outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, int flush) {
|
||||
strm.next_in = (char *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
int code;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
strm.next_out = outbuf;
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = BZ2_bzDecompress(&strm);
|
||||
break;
|
||||
case ENCODE:
|
||||
code = BZ2_bzCompress(&strm, flush);
|
||||
break;
|
||||
}
|
||||
if (code < 0) {
|
||||
LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
if (code == BZ_STREAM_END)
|
||||
return true;
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class bz_decoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_decoder(out_strm_ptr &&base) : bz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class bz_encoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_encoder(out_strm_ptr &&base) : bz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class lzma_strm : public filter_out_stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) override {
|
||||
return len == 0 || do_write(buf, len, LZMA_RUN);
|
||||
}
|
||||
|
||||
~lzma_strm() override {
|
||||
do_write(nullptr, 0, LZMA_FINISH);
|
||||
lzma_end(&strm);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum mode_t {
|
||||
DECODE,
|
||||
ENCODE_XZ,
|
||||
ENCODE_LZMA
|
||||
} mode;
|
||||
|
||||
lzma_strm(mode_t mode, out_strm_ptr &&base) :
|
||||
filter_out_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT), outbuf{0} {
|
||||
lzma_options_lzma opt;
|
||||
|
||||
// Initialize preset
|
||||
lzma_lzma_preset(&opt, 9);
|
||||
lzma_filter filters[] = {
|
||||
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
|
||||
{ .id = LZMA_VLI_UNKNOWN, .options = nullptr },
|
||||
};
|
||||
|
||||
lzma_ret code;
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
code = lzma_auto_decoder(&strm, UINT64_MAX, 0);
|
||||
break;
|
||||
case ENCODE_XZ:
|
||||
code = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
|
||||
break;
|
||||
case ENCODE_LZMA:
|
||||
code = lzma_alone_encoder(&strm, &opt);
|
||||
break;
|
||||
}
|
||||
if (code != LZMA_OK) {
|
||||
LOGE("LZMA initialization failed (%d)\n", code);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
lzma_stream strm;
|
||||
uint8_t outbuf[CHUNK];
|
||||
|
||||
bool do_write(const void *buf, size_t len, lzma_action flush) {
|
||||
strm.next_in = (uint8_t *) buf;
|
||||
strm.avail_in = len;
|
||||
do {
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
strm.next_out = outbuf;
|
||||
int code = lzma_code(&strm, flush);
|
||||
if (code != LZMA_OK && code != LZMA_STREAM_END) {
|
||||
LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code);
|
||||
return false;
|
||||
}
|
||||
if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
|
||||
return false;
|
||||
} while (strm.avail_out == 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class lzma_decoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_decoder(out_strm_ptr &&base) : lzma_strm(DECODE, std::move(base)) {}
|
||||
};
|
||||
|
||||
class xz_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit xz_encoder(out_strm_ptr &&base) : lzma_strm(ENCODE_XZ, std::move(base)) {}
|
||||
};
|
||||
|
||||
class lzma_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_encoder(out_strm_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {}
|
||||
};
|
||||
|
||||
class LZ4F_decoder : public filter_out_stream {
|
||||
public:
|
||||
explicit LZ4F_decoder(out_strm_ptr &&base) :
|
||||
filter_out_stream(std::move(base)), ctx(nullptr), outbuf(nullptr), outCapacity(0) {
|
||||
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
~LZ4F_decoder() override {
|
||||
LZ4F_freeDecompressionContext(ctx);
|
||||
delete[] outbuf;
|
||||
}
|
||||
|
||||
bool write(const void *buf, size_t len) override {
|
||||
auto in = reinterpret_cast<const uint8_t *>(buf);
|
||||
if (!outbuf) {
|
||||
size_t read = len;
|
||||
LZ4F_frameInfo_t info;
|
||||
LZ4F_getFrameInfo(ctx, &info, in, &read);
|
||||
switch (info.blockSizeID) {
|
||||
case LZ4F_default:
|
||||
case LZ4F_max64KB: outCapacity = 1 << 16; break;
|
||||
case LZ4F_max256KB: outCapacity = 1 << 18; break;
|
||||
case LZ4F_max1MB: outCapacity = 1 << 20; break;
|
||||
case LZ4F_max4MB: outCapacity = 1 << 22; break;
|
||||
}
|
||||
outbuf = new uint8_t[outCapacity];
|
||||
in += read;
|
||||
len -= read;
|
||||
}
|
||||
size_t read, write;
|
||||
LZ4F_errorCode_t code;
|
||||
do {
|
||||
read = len;
|
||||
write = outCapacity;
|
||||
code = LZ4F_decompress(ctx, outbuf, &write, in, &read, nullptr);
|
||||
if (LZ4F_isError(code)) {
|
||||
LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code));
|
||||
return false;
|
||||
}
|
||||
len -= read;
|
||||
in += read;
|
||||
if (!bwrite(outbuf, write))
|
||||
return false;
|
||||
} while (len != 0 || write != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
LZ4F_decompressionContext_t ctx;
|
||||
uint8_t *outbuf;
|
||||
size_t outCapacity;
|
||||
};
|
||||
|
||||
class LZ4F_encoder : public filter_out_stream {
|
||||
public:
|
||||
explicit LZ4F_encoder(out_strm_ptr &&base) :
|
||||
filter_out_stream(std::move(base)), ctx(nullptr), out_buf(nullptr), outCapacity(0) {
|
||||
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
bool write(const void *buf, size_t len) override {
|
||||
if (!out_buf) {
|
||||
LZ4F_preferences_t prefs {
|
||||
.frameInfo = {
|
||||
.blockSizeID = LZ4F_max4MB,
|
||||
.blockMode = LZ4F_blockIndependent,
|
||||
.contentChecksumFlag = LZ4F_contentChecksumEnabled,
|
||||
.blockChecksumFlag = LZ4F_noBlockChecksum,
|
||||
},
|
||||
.compressionLevel = 9,
|
||||
.autoFlush = 1,
|
||||
};
|
||||
outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs);
|
||||
out_buf = new uint8_t[outCapacity];
|
||||
size_t write = LZ4F_compressBegin(ctx, out_buf, outCapacity, &prefs);
|
||||
if (!bwrite(out_buf, write))
|
||||
return false;
|
||||
}
|
||||
if (len == 0)
|
||||
return true;
|
||||
|
||||
auto in = reinterpret_cast<const uint8_t *>(buf);
|
||||
size_t read, write;
|
||||
do {
|
||||
read = len > BLOCK_SZ ? BLOCK_SZ : len;
|
||||
write = LZ4F_compressUpdate(ctx, out_buf, outCapacity, in, read, nullptr);
|
||||
if (LZ4F_isError(write)) {
|
||||
LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write));
|
||||
return false;
|
||||
}
|
||||
len -= read;
|
||||
in += read;
|
||||
if (!bwrite(out_buf, write))
|
||||
return false;
|
||||
} while (len != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
~LZ4F_encoder() override {
|
||||
size_t len = LZ4F_compressEnd(ctx, out_buf, outCapacity, nullptr);
|
||||
if (LZ4F_isError(len)) {
|
||||
LOGE("LZ4F end of frame error: %s\n", LZ4F_getErrorName(len));
|
||||
} else if (!bwrite(out_buf, len)) {
|
||||
LOGE("LZ4F end of frame error: I/O error\n");
|
||||
}
|
||||
LZ4F_freeCompressionContext(ctx);
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
private:
|
||||
LZ4F_compressionContext_t ctx;
|
||||
uint8_t *out_buf;
|
||||
size_t outCapacity;
|
||||
|
||||
static constexpr size_t BLOCK_SZ = 1 << 22;
|
||||
};
|
||||
|
||||
class LZ4_decoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit LZ4_decoder(out_strm_ptr &&base) :
|
||||
chunk_out_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz)),
|
||||
out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {}
|
||||
|
||||
~LZ4_decoder() override {
|
||||
finalize();
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool) override {
|
||||
// This is an error
|
||||
if (len != chunk_sz)
|
||||
return false;
|
||||
|
||||
auto in = reinterpret_cast<const char *>(buf);
|
||||
|
||||
if (block_sz == 0) {
|
||||
memcpy(&block_sz, in, sizeof(block_sz));
|
||||
if (block_sz == 0x184C2102) {
|
||||
// This is actually the lz4 magic, read the next 4 bytes
|
||||
block_sz = 0;
|
||||
chunk_sz = sizeof(block_sz);
|
||||
return true;
|
||||
}
|
||||
// Read the next block chunk
|
||||
chunk_sz = block_sz;
|
||||
return true;
|
||||
} else {
|
||||
int r = LZ4_decompress_safe(in, out_buf, block_sz, LZ4_UNCOMPRESSED);
|
||||
chunk_sz = sizeof(block_sz);
|
||||
block_sz = 0;
|
||||
if (r < 0) {
|
||||
LOGW("LZ4HC decompression failure (%d)\n", r);
|
||||
return false;
|
||||
}
|
||||
return bwrite(out_buf, r);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
char *out_buf;
|
||||
uint32_t block_sz;
|
||||
};
|
||||
|
||||
class LZ4_encoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit LZ4_encoder(out_strm_ptr &&base, bool lg) :
|
||||
chunk_out_stream(std::move(base), LZ4_UNCOMPRESSED),
|
||||
out_buf(new char[LZ4_COMPRESSED]), lg(lg), in_total(0) {
|
||||
bwrite("\x02\x21\x4c\x18", 4);
|
||||
}
|
||||
|
||||
~LZ4_encoder() override {
|
||||
finalize();
|
||||
if (lg)
|
||||
bwrite(&in_total, sizeof(in_total));
|
||||
delete[] out_buf;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool write_chunk(const void *buf, size_t len, bool) override {
|
||||
auto in = static_cast<const char *>(buf);
|
||||
uint32_t block_sz = LZ4_compress_HC(in, out_buf, len, LZ4_COMPRESSED, LZ4HC_CLEVEL_MAX);
|
||||
if (block_sz == 0) {
|
||||
LOGW("LZ4HC compression failure\n");
|
||||
return false;
|
||||
}
|
||||
if (bwrite(&block_sz, sizeof(block_sz)) && bwrite(out_buf, block_sz)) {
|
||||
in_total += len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
char *out_buf;
|
||||
bool lg;
|
||||
uint32_t in_total;
|
||||
};
|
||||
|
||||
out_strm_ptr get_encoder(format_t type, out_strm_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
return make_unique<xz_encoder>(std::move(base));
|
||||
case LZMA:
|
||||
return make_unique<lzma_encoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_encoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_encoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
return make_unique<LZ4_encoder>(std::move(base), false);
|
||||
case LZ4_LG:
|
||||
return make_unique<LZ4_encoder>(std::move(base), true);
|
||||
case ZOPFLI:
|
||||
return make_unique<zopfli_encoder>(std::move(base));
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_encoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
out_strm_ptr get_decoder(format_t type, out_strm_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
case LZMA:
|
||||
return make_unique<lzma_decoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_decoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_decoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
case LZ4_LG:
|
||||
return make_unique<LZ4_decoder>(std::move(base));
|
||||
case ZOPFLI:
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_decoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
void decompress(char *infile, const char *outfile) {
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
out_strm_ptr strm;
|
||||
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
while ((len = read(in_fd, buf, sizeof(buf)))) {
|
||||
if (!strm) {
|
||||
format_t type = check_fmt(buf, len);
|
||||
|
||||
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
|
||||
|
||||
if (!COMPRESSED(type))
|
||||
LOGE("Input file is not a supported compressed type!\n");
|
||||
|
||||
/* If user does not provide outfile, infile has to be either
|
||||
* <path>.[ext], or '-'. Outfile will be either <path> or '-'.
|
||||
* If the input does not have proper format, abort */
|
||||
|
||||
char *ext = nullptr;
|
||||
if (outfile == nullptr) {
|
||||
outfile = infile;
|
||||
if (!in_std) {
|
||||
ext = strrchr(infile, '.');
|
||||
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
|
||||
LOGE("Input file is not a supported type!\n");
|
||||
|
||||
// Strip out extension and remove input
|
||||
*ext = '\0';
|
||||
rm_in = true;
|
||||
fprintf(stderr, "Decompressing to [%s]\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
strm = get_decoder(type, make_unique<fd_stream>(out_fd));
|
||||
if (ext) *ext = '.';
|
||||
}
|
||||
if (!strm->write(buf, len))
|
||||
LOGE("Decompression error!\n");
|
||||
}
|
||||
|
||||
strm.reset(nullptr);
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
void compress(const char *method, const char *infile, const char *outfile) {
|
||||
format_t fmt = name2fmt[method];
|
||||
if (fmt == UNKNOWN)
|
||||
LOGE("Unknown compression method: [%s]\n", method);
|
||||
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
|
||||
if (outfile == nullptr) {
|
||||
if (in_std) {
|
||||
out_fd = STDOUT_FILENO;
|
||||
} else {
|
||||
/* If user does not provide outfile and infile is not
|
||||
* STDIN, output to <infile>.[ext] */
|
||||
string tmp(infile);
|
||||
tmp += fmt2ext[fmt];
|
||||
out_fd = xopen(tmp.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
|
||||
rm_in = true;
|
||||
}
|
||||
} else {
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
auto strm = get_encoder(fmt, make_unique<fd_stream>(out_fd));
|
||||
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
while ((len = read(in_fd, buf, sizeof(buf)))) {
|
||||
if (!strm->write(buf, len))
|
||||
LOGE("Compression error!\n");
|
||||
}
|
||||
|
||||
strm.reset(nullptr);
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
bool decompress(rust::Slice<const uint8_t> buf, int fd) {
|
||||
format_t type = check_fmt(buf.data(), buf.length());
|
||||
|
||||
if (!COMPRESSED(type)) {
|
||||
LOGE("Input file is not a supported compression format!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto strm = get_decoder(type, make_unique<fd_stream>(fd));
|
||||
if (!strm->write(buf.data(), buf.length())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out) {
|
||||
auto strm = get_encoder(XZ, make_unique<rust_vec_stream>(out));
|
||||
if (!strm->write(buf.data(), buf.length())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unxz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out) {
|
||||
format_t type = check_fmt(buf.data(), buf.length());
|
||||
if (type != XZ) {
|
||||
LOGE("Input file is not in xz format!\n");
|
||||
return false;
|
||||
}
|
||||
auto strm = get_decoder(XZ, make_unique<rust_vec_stream>(out));
|
||||
if (!strm->write(buf.data(), buf.length())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cxx.h>
|
||||
#include <stream.hpp>
|
||||
|
||||
#include "format.hpp"
|
||||
|
||||
out_strm_ptr get_encoder(format_t type, out_strm_ptr &&base);
|
||||
out_strm_ptr get_decoder(format_t type, out_strm_ptr &&base);
|
||||
void compress(const char *method, const char *infile, const char *outfile);
|
||||
void decompress(char *infile, const char *outfile);
|
||||
bool decompress(rust::Slice<const uint8_t> buf, int fd);
|
||||
bool xz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
||||
bool unxz(rust::Slice<const uint8_t> buf, rust::Vec<uint8_t> &out);
|
||||
@@ -0,0 +1,426 @@
|
||||
use crate::ffi::FileFormat;
|
||||
use base::{Chunker, LoggedResult, WriteExt};
|
||||
use bytemuck::bytes_of_mut;
|
||||
use bzip2::{Compression as BzCompression, write::BzDecoder, write::BzEncoder};
|
||||
use flate2::{Compression as GzCompression, write::GzEncoder, write::MultiGzDecoder};
|
||||
use lz4::{
|
||||
BlockMode, BlockSize, ContentChecksum, Encoder as LZ4FrameEncoder,
|
||||
EncoderBuilder as LZ4FrameEncoderBuilder, block::CompressionMode, liblz4::BlockChecksum,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use xz2::{
|
||||
stream::{Check as LzmaCheck, Filters as LzmaFilters, LzmaOptions, Stream as LzmaStream},
|
||||
write::{XzDecoder, XzEncoder},
|
||||
};
|
||||
use zopfli::{BlockType, GzipEncoder as ZopFliEncoder, Options as ZopfliOptions};
|
||||
|
||||
pub trait WriteFinish<W: Write>: Write {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W>;
|
||||
}
|
||||
|
||||
// Boilerplate for existing types
|
||||
|
||||
macro_rules! finish_impl {
|
||||
($($t:ty),*) => {$(
|
||||
impl<W: Write> WriteFinish<W> for $t {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
Self::finish(*self)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
finish_impl!(GzEncoder<W>, MultiGzDecoder<W>, BzEncoder<W>, XzEncoder<W>);
|
||||
|
||||
macro_rules! finish_impl_ref {
|
||||
($($t:ty),*) => {$(
|
||||
impl<W: Write> WriteFinish<W> for $t {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
Self::finish(self.as_mut())
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
finish_impl_ref!(BzDecoder<W>, XzDecoder<W>);
|
||||
|
||||
impl<W: Write> WriteFinish<W> for BufWriter<ZopFliEncoder<W>> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
let inner = self.into_inner()?;
|
||||
ZopFliEncoder::finish(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4FrameEncoder<W> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
let (w, r) = Self::finish(*self);
|
||||
r?;
|
||||
Ok(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Adapt Reader to Writer
|
||||
|
||||
// In case some decoders don't support the Write trait, instead of pushing data into the
|
||||
// decoder, we have no choice but to pull data out of it. So first, we create a "fake" reader
|
||||
// that does not own any data as a placeholder. In the Writer adapter struct, when data
|
||||
// is fed in, we call FakeReader::set_data to forward this data as the "source" of the
|
||||
// decoder. Next, we pull data out of the decoder, and finally, forward the decoded data to output.
|
||||
|
||||
struct FakeReader(Cell<&'static [u8]>);
|
||||
|
||||
impl FakeReader {
|
||||
fn new() -> FakeReader {
|
||||
FakeReader(Cell::new(&[]))
|
||||
}
|
||||
|
||||
// SAFETY: the lifetime of the buffer is between the invocation of
|
||||
// this method and the invocation of FakeReader::clear. There is currently
|
||||
// no way to represent this with Rust's lifetime marker, so we transmute all
|
||||
// lifetimes away and make the users of this struct manually manage the lifetime.
|
||||
// It is the responsibility of the caller to ensure the underlying reference does not
|
||||
// live longer than it should.
|
||||
unsafe fn set_data(&self, data: &[u8]) {
|
||||
let buf: &'static [u8] = unsafe { std::mem::transmute(data) };
|
||||
self.0.set(buf)
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
self.0.set(&[])
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FakeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let data = self.0.get();
|
||||
let len = std::cmp::min(buf.len(), data.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
self.0.set(&data[len..]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
// LZ4FrameDecoder
|
||||
|
||||
struct LZ4FrameDecoder<W: Write> {
|
||||
write: W,
|
||||
decoder: lz4::Decoder<FakeReader>,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4FrameDecoder<W> {
|
||||
fn new(write: W) -> Self {
|
||||
let fake = FakeReader::new();
|
||||
let decoder = lz4::Decoder::new(fake).unwrap();
|
||||
LZ4FrameDecoder { write, decoder }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LZ4FrameDecoder<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write_all(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
// SAFETY: buf is removed from the reader immediately after usage
|
||||
unsafe { self.decoder.reader().set_data(buf) };
|
||||
std::io::copy(&mut self.decoder, &mut self.write)?;
|
||||
self.decoder.reader().clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4FrameDecoder<W> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
let (_, r) = self.decoder.finish();
|
||||
r?;
|
||||
Ok(self.write)
|
||||
}
|
||||
}
|
||||
|
||||
// LZ4BlockArchive format
|
||||
//
|
||||
// len: | 4 | 4 | n | ... | 4 |
|
||||
// data: | magic | compressed block size | compressed block data | ... | total uncompressed size |
|
||||
|
||||
// LZ4BlockEncoder
|
||||
|
||||
const LZ4_BLOCK_SIZE: usize = 0x800000;
|
||||
const LZ4HC_CLEVEL_MAX: i32 = 12;
|
||||
const LZ4_MAGIC: &[u8] = b"\x02\x21\x4c\x18";
|
||||
|
||||
struct LZ4BlockEncoder<W: Write> {
|
||||
write: W,
|
||||
chunker: Chunker,
|
||||
out_buf: Box<[u8]>,
|
||||
total: u32,
|
||||
is_lg: bool,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4BlockEncoder<W> {
|
||||
fn new(write: W, is_lg: bool) -> Self {
|
||||
let out_sz = lz4::block::compress_bound(LZ4_BLOCK_SIZE).unwrap_or(LZ4_BLOCK_SIZE);
|
||||
LZ4BlockEncoder {
|
||||
write,
|
||||
chunker: Chunker::new(LZ4_BLOCK_SIZE),
|
||||
// SAFETY: all bytes will be initialized before it is used
|
||||
out_buf: unsafe { Box::new_uninit_slice(out_sz).assume_init() },
|
||||
total: 0,
|
||||
is_lg,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_block(write: &mut W, out_buf: &mut [u8], chunk: &[u8]) -> std::io::Result<()> {
|
||||
let compressed_size = lz4::block::compress_to_buffer(
|
||||
chunk,
|
||||
Some(CompressionMode::HIGHCOMPRESSION(LZ4HC_CLEVEL_MAX)),
|
||||
false,
|
||||
out_buf,
|
||||
)?;
|
||||
let block_size = compressed_size as u32;
|
||||
write.write_pod(&block_size)?;
|
||||
write.write_all(&out_buf[..compressed_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LZ4BlockEncoder<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write_all(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> {
|
||||
if self.total == 0 {
|
||||
// Write header
|
||||
self.write.write_all(LZ4_MAGIC)?;
|
||||
}
|
||||
|
||||
self.total += buf.len() as u32;
|
||||
while !buf.is_empty() {
|
||||
let (b, chunk) = self.chunker.add_data(buf);
|
||||
buf = b;
|
||||
if let Some(chunk) = chunk {
|
||||
Self::encode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4BlockEncoder<W> {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
let chunk = self.chunker.get_available();
|
||||
if !chunk.is_empty() {
|
||||
Self::encode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
}
|
||||
if self.is_lg {
|
||||
self.write.write_pod(&self.total)?;
|
||||
}
|
||||
Ok(self.write)
|
||||
}
|
||||
}
|
||||
|
||||
// LZ4BlockDecoder
|
||||
|
||||
struct LZ4BlockDecoder<W: Write> {
|
||||
write: W,
|
||||
chunker: Chunker,
|
||||
out_buf: Box<[u8]>,
|
||||
curr_block_size: usize,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4BlockDecoder<W> {
|
||||
fn new(write: W) -> Self {
|
||||
LZ4BlockDecoder {
|
||||
write,
|
||||
chunker: Chunker::new(size_of::<u32>()),
|
||||
// SAFETY: all bytes will be initialized before it is used
|
||||
out_buf: unsafe { Box::new_uninit_slice(LZ4_BLOCK_SIZE).assume_init() },
|
||||
curr_block_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_block(write: &mut W, out_buf: &mut [u8], chunk: &[u8]) -> std::io::Result<()> {
|
||||
let decompressed_size =
|
||||
lz4::block::decompress_to_buffer(chunk, Some(LZ4_BLOCK_SIZE as i32), out_buf)?;
|
||||
write.write_all(&out_buf[..decompressed_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LZ4BlockDecoder<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write_all(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let (b, chunk) = self.chunker.add_data(buf);
|
||||
buf = b;
|
||||
if let Some(chunk) = chunk {
|
||||
if chunk == LZ4_MAGIC {
|
||||
// Skip magic, read next u32
|
||||
continue;
|
||||
}
|
||||
if self.curr_block_size == 0 {
|
||||
// We haven't got the current block size yet, try read it
|
||||
let mut next_u32: u32 = 0;
|
||||
bytes_of_mut(&mut next_u32).copy_from_slice(chunk);
|
||||
|
||||
if next_u32 > lz4::block::compress_bound(LZ4_BLOCK_SIZE)? as u32 {
|
||||
// This is the LG format trailer, EOF
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update chunker to read next block
|
||||
self.curr_block_size = next_u32 as usize;
|
||||
self.chunker.set_chunk_size(self.curr_block_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Actually decode the block
|
||||
Self::decode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
|
||||
// Reset for the next block
|
||||
self.curr_block_size = 0;
|
||||
self.chunker.set_chunk_size(size_of::<u32>());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4BlockDecoder<W> {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
let chunk = self.chunker.get_available();
|
||||
if !chunk.is_empty() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Interrupted,
|
||||
"Finish ran before end of compressed stream",
|
||||
));
|
||||
}
|
||||
Ok(self.write)
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level APIs
|
||||
|
||||
pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
|
||||
match format {
|
||||
FileFormat::XZ => {
|
||||
let opt = LzmaOptions::new_preset(9).unwrap();
|
||||
let stream =
|
||||
LzmaStream::new_stream_encoder(LzmaFilters::new().lzma2(&opt), LzmaCheck::Crc32)
|
||||
.unwrap();
|
||||
Box::new(XzEncoder::new_stream(w, stream))
|
||||
}
|
||||
FileFormat::LZMA => {
|
||||
let opt = LzmaOptions::new_preset(9).unwrap();
|
||||
let stream = LzmaStream::new_lzma_encoder(&opt).unwrap();
|
||||
Box::new(XzEncoder::new_stream(w, stream))
|
||||
}
|
||||
FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())),
|
||||
FileFormat::LZ4 => {
|
||||
let encoder = LZ4FrameEncoderBuilder::new()
|
||||
.block_size(BlockSize::Max4MB)
|
||||
.block_mode(BlockMode::Independent)
|
||||
.checksum(ContentChecksum::ChecksumEnabled)
|
||||
.block_checksum(BlockChecksum::BlockChecksumEnabled)
|
||||
.level(9)
|
||||
.auto_flush(true)
|
||||
.build(w)
|
||||
.unwrap();
|
||||
Box::new(encoder)
|
||||
}
|
||||
FileFormat::LZ4_LEGACY => Box::new(LZ4BlockEncoder::new(w, false)),
|
||||
FileFormat::LZ4_LG => Box::new(LZ4BlockEncoder::new(w, true)),
|
||||
FileFormat::ZOPFLI => {
|
||||
// These options are already better than gzip -9
|
||||
let opt = ZopfliOptions {
|
||||
iteration_count: NonZeroU64::new(1).unwrap(),
|
||||
maximum_block_splits: 1,
|
||||
..Default::default()
|
||||
};
|
||||
Box::new(ZopFliEncoder::new_buffered(opt, BlockType::Dynamic, w).unwrap())
|
||||
}
|
||||
FileFormat::GZIP => Box::new(GzEncoder::new(w, GzCompression::best())),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_decoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
|
||||
match format {
|
||||
FileFormat::XZ | FileFormat::LZMA => {
|
||||
let stream = LzmaStream::new_auto_decoder(u64::MAX, 0).unwrap();
|
||||
Box::new(XzDecoder::new_stream(w, stream))
|
||||
}
|
||||
FileFormat::BZIP2 => Box::new(BzDecoder::new(w)),
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(w)),
|
||||
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(w)),
|
||||
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(w)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// C++ FFI
|
||||
|
||||
pub fn compress_fd(format: FileFormat, in_fd: RawFd, out_fd: RawFd) {
|
||||
let mut in_file = unsafe { ManuallyDrop::new(File::from_raw_fd(in_fd)) };
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut encoder = get_encoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
std::io::copy(in_file.deref_mut(), encoder.as_mut())?;
|
||||
encoder.finish()?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: RawFd, out_fd: RawFd) {
|
||||
let mut in_file = unsafe { ManuallyDrop::new(File::from_raw_fd(in_fd)) };
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut decoder = get_decoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
decoder.write_all(in_bytes)?;
|
||||
std::io::copy(in_file.deref_mut(), decoder.as_mut())?;
|
||||
decoder.finish()?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut encoder = get_encoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
encoder.write_all(in_bytes)?;
|
||||
encoder.finish()?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut decoder = get_decoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
decoder.write_all(in_bytes)?;
|
||||
decoder.finish()?;
|
||||
};
|
||||
}
|
||||
+21
-11
@@ -25,7 +25,8 @@ use base::{
|
||||
};
|
||||
|
||||
use crate::check_env;
|
||||
use crate::ffi::{unxz, xz};
|
||||
use crate::compress::{get_decoder, get_encoder};
|
||||
use crate::ffi::FileFormat;
|
||||
use crate::patch::{patch_encryption, patch_verity};
|
||||
|
||||
#[derive(FromArgs)]
|
||||
@@ -308,7 +309,7 @@ impl Cpio {
|
||||
}
|
||||
pos += file.write(
|
||||
format!("070701{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}{:08x}",
|
||||
inode, 0o755, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0
|
||||
inode, 0o755, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0
|
||||
).as_bytes()
|
||||
)?;
|
||||
pos += file.write("TRAILER!!!\0".as_bytes())?;
|
||||
@@ -695,12 +696,16 @@ impl CpioEntry {
|
||||
if self.mode & S_IFMT != S_IFREG {
|
||||
return false;
|
||||
}
|
||||
let mut compressed = Vec::new();
|
||||
if !xz(&self.data, &mut compressed) {
|
||||
let mut encoder = get_encoder(FileFormat::XZ, Vec::new());
|
||||
let Ok(data): std::io::Result<Vec<u8>> = (try {
|
||||
encoder.write_all(&self.data)?;
|
||||
encoder.finish()?
|
||||
}) else {
|
||||
eprintln!("xz compression failed");
|
||||
return false;
|
||||
}
|
||||
self.data = compressed;
|
||||
};
|
||||
|
||||
self.data = data;
|
||||
true
|
||||
}
|
||||
|
||||
@@ -708,12 +713,17 @@ impl CpioEntry {
|
||||
if self.mode & S_IFMT != S_IFREG {
|
||||
return false;
|
||||
}
|
||||
let mut decompressed = Vec::new();
|
||||
if !unxz(&self.data, &mut decompressed) {
|
||||
eprintln!("xz decompression failed");
|
||||
|
||||
let mut decoder = get_decoder(FileFormat::XZ, Vec::new());
|
||||
let Ok(data): std::io::Result<Vec<u8>> = (try {
|
||||
decoder.write_all(&self.data)?;
|
||||
decoder.finish()?
|
||||
}) else {
|
||||
eprintln!("xz compression failed");
|
||||
return false;
|
||||
}
|
||||
self.data = decompressed;
|
||||
};
|
||||
|
||||
self.data = data;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
+50
-49
@@ -1,3 +1,4 @@
|
||||
#include "boot-rs.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
Name2Fmt name2fmt;
|
||||
@@ -6,88 +7,88 @@ Fmt2Ext fmt2ext;
|
||||
|
||||
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
||||
|
||||
format_t check_fmt(const void *buf, size_t len) {
|
||||
FileFormat check_fmt(const void *buf, size_t len) {
|
||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||
return CHROMEOS;
|
||||
return FileFormat::CHROMEOS;
|
||||
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
||||
return AOSP;
|
||||
return FileFormat::AOSP;
|
||||
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
||||
return AOSP_VENDOR;
|
||||
return FileFormat::AOSP_VENDOR;
|
||||
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
||||
return GZIP;
|
||||
return FileFormat::GZIP;
|
||||
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
||||
return LZOP;
|
||||
return FileFormat::LZOP;
|
||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||
return XZ;
|
||||
return FileFormat::XZ;
|
||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
return LZMA;
|
||||
return FileFormat::LZMA;
|
||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||
return BZIP2;
|
||||
return FileFormat::BZIP2;
|
||||
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
||||
return LZ4;
|
||||
return FileFormat::LZ4;
|
||||
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
||||
return LZ4_LEGACY;
|
||||
return FileFormat::LZ4_LEGACY;
|
||||
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
||||
return MTK;
|
||||
return FileFormat::MTK;
|
||||
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
||||
return DTB;
|
||||
return FileFormat::DTB;
|
||||
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
||||
return DHTB;
|
||||
return FileFormat::DHTB;
|
||||
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
||||
return BLOB;
|
||||
return FileFormat::BLOB;
|
||||
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
||||
return ZIMAGE;
|
||||
return FileFormat::ZIMAGE;
|
||||
} else {
|
||||
return UNKNOWN;
|
||||
return FileFormat::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Name::operator[](format_t fmt) {
|
||||
const char *Fmt2Name::operator[](FileFormat fmt) {
|
||||
switch (fmt) {
|
||||
case GZIP:
|
||||
case FileFormat::GZIP:
|
||||
return "gzip";
|
||||
case ZOPFLI:
|
||||
case FileFormat::ZOPFLI:
|
||||
return "zopfli";
|
||||
case LZOP:
|
||||
case FileFormat::LZOP:
|
||||
return "lzop";
|
||||
case XZ:
|
||||
case FileFormat::XZ:
|
||||
return "xz";
|
||||
case LZMA:
|
||||
case FileFormat::LZMA:
|
||||
return "lzma";
|
||||
case BZIP2:
|
||||
case FileFormat::BZIP2:
|
||||
return "bzip2";
|
||||
case LZ4:
|
||||
case FileFormat::LZ4:
|
||||
return "lz4";
|
||||
case LZ4_LEGACY:
|
||||
case FileFormat::LZ4_LEGACY:
|
||||
return "lz4_legacy";
|
||||
case LZ4_LG:
|
||||
case FileFormat::LZ4_LG:
|
||||
return "lz4_lg";
|
||||
case DTB:
|
||||
case FileFormat::DTB:
|
||||
return "dtb";
|
||||
case ZIMAGE:
|
||||
case FileFormat::ZIMAGE:
|
||||
return "zimage";
|
||||
default:
|
||||
return "raw";
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Ext::operator[](format_t fmt) {
|
||||
const char *Fmt2Ext::operator[](FileFormat fmt) {
|
||||
switch (fmt) {
|
||||
case GZIP:
|
||||
case ZOPFLI:
|
||||
case FileFormat::GZIP:
|
||||
case FileFormat::ZOPFLI:
|
||||
return ".gz";
|
||||
case LZOP:
|
||||
case FileFormat::LZOP:
|
||||
return ".lzo";
|
||||
case XZ:
|
||||
case FileFormat::XZ:
|
||||
return ".xz";
|
||||
case LZMA:
|
||||
case FileFormat::LZMA:
|
||||
return ".lzma";
|
||||
case BZIP2:
|
||||
case FileFormat::BZIP2:
|
||||
return ".bz2";
|
||||
case LZ4:
|
||||
case LZ4_LEGACY:
|
||||
case LZ4_LG:
|
||||
case FileFormat::LZ4:
|
||||
case FileFormat::LZ4_LEGACY:
|
||||
case FileFormat::LZ4_LG:
|
||||
return ".lz4";
|
||||
default:
|
||||
return "";
|
||||
@@ -96,15 +97,15 @@ const char *Fmt2Ext::operator[](format_t fmt) {
|
||||
|
||||
#define CHECK(s, f) else if (name == s) return f;
|
||||
|
||||
format_t Name2Fmt::operator[](std::string_view name) {
|
||||
FileFormat Name2Fmt::operator[](std::string_view name) {
|
||||
if (0) {}
|
||||
CHECK("gzip", GZIP)
|
||||
CHECK("zopfli", ZOPFLI)
|
||||
CHECK("xz", XZ)
|
||||
CHECK("lzma", LZMA)
|
||||
CHECK("bzip2", BZIP2)
|
||||
CHECK("lz4", LZ4)
|
||||
CHECK("lz4_legacy", LZ4_LEGACY)
|
||||
CHECK("lz4_lg", LZ4_LG)
|
||||
else return UNKNOWN;
|
||||
CHECK("gzip", FileFormat::GZIP)
|
||||
CHECK("zopfli", FileFormat::ZOPFLI)
|
||||
CHECK("xz", FileFormat::XZ)
|
||||
CHECK("lzma", FileFormat::LZMA)
|
||||
CHECK("bzip2", FileFormat::BZIP2)
|
||||
CHECK("lz4", FileFormat::LZ4)
|
||||
CHECK("lz4_legacy", FileFormat::LZ4_LEGACY)
|
||||
CHECK("lz4_lg", FileFormat::LZ4_LG)
|
||||
else return FileFormat::UNKNOWN;
|
||||
}
|
||||
|
||||
+11
-30
@@ -2,33 +2,10 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
/* Boot formats */
|
||||
CHROMEOS,
|
||||
AOSP,
|
||||
AOSP_VENDOR,
|
||||
DHTB,
|
||||
BLOB,
|
||||
/* Compression formats */
|
||||
GZIP,
|
||||
ZOPFLI,
|
||||
XZ,
|
||||
LZMA,
|
||||
BZIP2,
|
||||
LZ4,
|
||||
LZ4_LEGACY,
|
||||
LZ4_LG,
|
||||
/* Unsupported compression */
|
||||
LZOP,
|
||||
/* Misc */
|
||||
MTK,
|
||||
DTB,
|
||||
ZIMAGE,
|
||||
} format_t;
|
||||
enum class FileFormat : ::std::uint8_t;
|
||||
|
||||
#define COMPRESSED(fmt) ((fmt) >= GZIP && (fmt) < LZOP)
|
||||
#define COMPRESSED_ANY(fmt) ((fmt) >= GZIP && (fmt) <= LZOP)
|
||||
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
|
||||
#define COMPRESSED_ANY(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) <= +FileFormat::LZOP)
|
||||
|
||||
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
||||
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
|
||||
@@ -66,20 +43,24 @@ typedef enum {
|
||||
|
||||
class Fmt2Name {
|
||||
public:
|
||||
const char *operator[](format_t fmt);
|
||||
const char *operator[](FileFormat fmt);
|
||||
};
|
||||
|
||||
class Fmt2Ext {
|
||||
public:
|
||||
const char *operator[](format_t fmt);
|
||||
const char *operator[](FileFormat fmt);
|
||||
};
|
||||
|
||||
class Name2Fmt {
|
||||
public:
|
||||
format_t operator[](std::string_view name);
|
||||
FileFormat operator[](std::string_view name);
|
||||
};
|
||||
|
||||
format_t check_fmt(const void *buf, size_t len);
|
||||
FileFormat check_fmt(const void *buf, size_t len);
|
||||
|
||||
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
||||
return check_fmt(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
extern Name2Fmt name2fmt;
|
||||
extern Fmt2Name fmt2name;
|
||||
|
||||
+33
-6
@@ -4,15 +4,15 @@
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use base;
|
||||
use compress::{compress_bytes, compress_fd, decompress_bytes, decompress_bytes_fd};
|
||||
use cpio::cpio_commands;
|
||||
use dtb::dtb_commands;
|
||||
pub use libbz2_rs_sys::*;
|
||||
pub use libz_rs_sys::*;
|
||||
use patch::hexpatch;
|
||||
use payload::extract_boot_from_payload;
|
||||
use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image};
|
||||
use std::env;
|
||||
|
||||
mod compress;
|
||||
mod cpio;
|
||||
mod dtb;
|
||||
mod patch;
|
||||
@@ -24,6 +24,31 @@ mod sign;
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod ffi {
|
||||
enum FileFormat {
|
||||
UNKNOWN,
|
||||
/* Boot formats */
|
||||
CHROMEOS,
|
||||
AOSP,
|
||||
AOSP_VENDOR,
|
||||
DHTB,
|
||||
BLOB,
|
||||
/* Compression formats */
|
||||
GZIP,
|
||||
ZOPFLI,
|
||||
XZ,
|
||||
LZMA,
|
||||
BZIP2,
|
||||
LZ4,
|
||||
LZ4_LEGACY,
|
||||
LZ4_LG,
|
||||
/* Unsupported compression */
|
||||
LZOP,
|
||||
/* Misc */
|
||||
MTK,
|
||||
DTB,
|
||||
ZIMAGE,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("../base/include/base.hpp");
|
||||
|
||||
@@ -33,10 +58,8 @@ pub mod ffi {
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("compress.hpp");
|
||||
fn decompress(buf: &[u8], fd: i32) -> bool;
|
||||
fn xz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
||||
fn unxz(buf: &[u8], out: &mut Vec<u8>) -> bool;
|
||||
include!("format.hpp");
|
||||
fn check_fmt(buf: &[u8]) -> FileFormat;
|
||||
|
||||
include!("bootimg.hpp");
|
||||
#[cxx_name = "boot_img"]
|
||||
@@ -57,6 +80,10 @@ pub mod ffi {
|
||||
fn sha256_hash(data: &[u8], out: &mut [u8]);
|
||||
|
||||
fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool;
|
||||
fn compress_fd(format: FileFormat, in_fd: i32, out_fd: i32);
|
||||
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||
fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: i32, out_fd: i32);
|
||||
}
|
||||
|
||||
#[namespace = "rust"]
|
||||
|
||||
+89
-12
@@ -2,22 +2,12 @@
|
||||
|
||||
#include "boot-rs.hpp"
|
||||
#include "magiskboot.hpp"
|
||||
#include "compress.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef USE_CRT0
|
||||
__BEGIN_DECLS
|
||||
int musl_vfprintf(FILE *stream, const char *format, va_list arg);
|
||||
int vfprintf(FILE *stream, const char *format, va_list arg) {
|
||||
return musl_vfprintf(stream, format, arg);
|
||||
}
|
||||
__END_DECLS
|
||||
#endif
|
||||
|
||||
static void print_formats() {
|
||||
for (int fmt = GZIP; fmt < LZOP; ++fmt) {
|
||||
fprintf(stderr, "%s ", fmt2name[(format_t) fmt]);
|
||||
for (int fmt = +FileFormat::GZIP; fmt < +FileFormat::LZOP; ++fmt) {
|
||||
fprintf(stderr, "%s ", fmt2name[(FileFormat) fmt]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +116,93 @@ Supported actions:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void decompress(char *infile, const char *outfile) {
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
|
||||
uint8_t buf[4096];
|
||||
size_t len = read(in_fd, buf, sizeof(buf));
|
||||
FileFormat type = check_fmt(buf, len);
|
||||
|
||||
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
|
||||
|
||||
if (!COMPRESSED(type))
|
||||
LOGE("Input file is not a supported compressed type!\n");
|
||||
|
||||
// If user does not provide outfile, infile has to be either
|
||||
// <path>.[ext], or '-'. Outfile will be either <path> or '-'.
|
||||
// If the input does not have proper format, abort.
|
||||
|
||||
char *ext = nullptr;
|
||||
if (outfile == nullptr) {
|
||||
outfile = infile;
|
||||
if (!in_std) {
|
||||
ext = strrchr(infile, '.');
|
||||
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
|
||||
LOGE("Input file is not a supported type!\n");
|
||||
|
||||
// Strip out extension and remove input
|
||||
*ext = '\0';
|
||||
rm_in = true;
|
||||
fprintf(stderr, "Decompressing to [%s]\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (ext) *ext = '.';
|
||||
|
||||
decompress_bytes_fd(type, byte_view{ buf, len }, in_fd, out_fd);
|
||||
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
static void compress(const char *method, const char *infile, const char *outfile) {
|
||||
FileFormat fmt = name2fmt[method];
|
||||
if (fmt == FileFormat::UNKNOWN)
|
||||
LOGE("Unknown compression method: [%s]\n", method);
|
||||
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
|
||||
if (outfile == nullptr) {
|
||||
if (in_std) {
|
||||
out_fd = STDOUT_FILENO;
|
||||
} else {
|
||||
// If user does not provide outfile and infile is not
|
||||
// STDIN, output to <infile>.[ext]
|
||||
string tmp(infile);
|
||||
tmp += fmt2ext[fmt];
|
||||
out_fd = xopen(tmp.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
|
||||
rm_in = true;
|
||||
}
|
||||
} else {
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
compress_fd(fmt, in_fd, out_fd);
|
||||
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
cmdline_logging();
|
||||
umask(0);
|
||||
|
||||
+11
-10
@@ -1,16 +1,14 @@
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use quick_protobuf::{BytesReader, MessageRead};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||
os::fd::{AsRawFd, FromRawFd},
|
||||
os::fd::FromRawFd,
|
||||
};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use quick_protobuf::{BytesReader, MessageRead};
|
||||
|
||||
use crate::{
|
||||
ffi,
|
||||
proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type},
|
||||
};
|
||||
use crate::compress::get_decoder;
|
||||
use crate::ffi::check_fmt;
|
||||
use crate::proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type};
|
||||
use base::{
|
||||
LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef,
|
||||
};
|
||||
@@ -168,9 +166,12 @@ fn do_extract_boot_from_payload(
|
||||
}
|
||||
Type::REPLACE_BZ | Type::REPLACE_XZ => {
|
||||
out_file.seek(SeekFrom::Start(out_offset))?;
|
||||
if !ffi::decompress(data, out_file.as_raw_fd()) {
|
||||
let mut decoder = get_decoder(check_fmt(data), &mut out_file);
|
||||
let Ok(_): std::io::Result<()> = (try {
|
||||
decoder.write_all(data)?;
|
||||
}) else {
|
||||
return Err(bad_payload!("decompression failed"));
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => return Err(bad_payload!("unsupported operation type")),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user