Migrate all compression code to Rust

This commit is contained in:
topjohnwu
2025-05-01 02:28:00 -07:00
committed by John Wu
parent 527bbc0368
commit 78d1200608
26 changed files with 1871 additions and 1265 deletions
+5 -2
View File
@@ -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
View File
@@ -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());
}
+5 -5
View File
@@ -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; }
-287
View File
@@ -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 ---*/
/*-------------------------------------------------------------*/
-762
View File
@@ -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;
}
-14
View File
@@ -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);
+426
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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")),
};