diff --git a/Makefile b/Makefile index 1b1a4a0..2168098 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ LIB_SRCS = \ LIB_OBJS = $(LIB_SRCS:%.cpp=%.o) LIB_INCS = -Iinclude -Iandroid-base/include -LDFLAGS += -L. -l$(LIB_NAME) -lm -lz +LDFLAGS += -L. -l$(LIB_NAME) -lm -lz -llz4 BINS = simg2img simg2simg img2simg append2simg HEADERS = include/sparse/sparse.h diff --git a/README.md b/README.md index 7bca403..f642407 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,16 @@ $ file /output/path/system.raw.img system.raw.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files) ``` +Vendor extensions +----------------- + +Sparse library was additionally patched to support Sony SIN images containing LZ4 compressed chunks of 0xCAC5 type. + Windows ------- If you want to build simg2img on Windows you'll need to [install MinGW](http://www.mingw.org/wiki/howto_install_the_mingw_gcc_compiler_suite) -and also zlib and libasprintf (go to MinGW Libraries in the installer and check `mingw32-libz` and `mingw32-libasprintf`). +and also zlib, liblz4, and libasprintf (go to MinGW Libraries in the installer and check `mingw32-libz`, `mingw32-lz4`, and `mingw32-libasprintf`). Once you've done that run the following command to build simg2img: ``` @@ -31,8 +36,8 @@ CFLAGS=-DUSE_MINGW LDFLAGS=-lasprintf mingw32-make Linux ------ -If zlib.h is missing then install it using +If zlib.h and (or) lz4.h are missing then install them using -Ubuntu: ```sudo apt-get install libz-dev``` +Ubuntu: ```sudo apt-get install libz-dev liblz4-dev``` -Fedora: ```sudo dnf install libz-devel``` +Fedora: ```sudo dnf install libz-devel lz4-devel``` diff --git a/sparse_format.h b/sparse_format.h index a8a721e..a67354c 100644 --- a/sparse_format.h +++ b/sparse_format.h @@ -42,6 +42,7 @@ typedef struct sparse_header { #define CHUNK_TYPE_FILL 0xCAC2 #define CHUNK_TYPE_DONT_CARE 0xCAC3 #define CHUNK_TYPE_CRC32 0xCAC4 +#define CHUNK_TYPE_SIN_LZ4 0xCAC5 /* Sony Xperia (SIN image) specific type */ typedef struct chunk_header { __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ diff --git a/sparse_read.cpp b/sparse_read.cpp index c4c1823..197fed3 100644 --- a/sparse_read.cpp +++ b/sparse_read.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -283,6 +284,55 @@ static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size return 0; } +static int process_sin_lz4_chunk(struct sparse_file* s, unsigned int chunk_size, + SparseFileSource* source, unsigned int blocks, unsigned int block, + uint32_t* crc32) { + int64_t len = blocks * s->block_size; + char* ibuf = (char*)malloc(chunk_size); + char* obuf = (char*)malloc(len); + int ret = 0; + + if (!ibuf || !obuf) { + ret = -ENOMEM; + goto exit; + } + + ret = source->ReadValue(ibuf, chunk_size); + if (ret < 0) + goto exit; + + /* Chunk data is a huge (tens of megabytes) LZ4 block, so pass it directly + * to the LZ4 decompress. + * + * NB: there are 12 trailing bytes of unclear nature. LZ4 decompress code + * deals with them by properly stop at the final 5-literals sequence so it's + * safe to pass the whole blob as it is. Subject for future study. + */ + ret = LZ4_decompress_safe(ibuf, obuf, chunk_size, len); + if (ret < 0 || ret != len) { + ret = -EINVAL; + goto exit; + } + + free(ibuf); /* Release input memory ASAP */ + ibuf = nullptr; + + ret = sparse_file_add_data(s, obuf, len, block); + if (ret < 0) + goto exit; + + if (crc32) + *crc32 = sparse_crc32(*crc32, obuf, len); + + obuf = nullptr; /* Forget (prevent freeing of) delivered block */ + +exit: + free(ibuf); + free(obuf); + + return ret; +} + static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz, chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) { int ret; @@ -325,6 +375,14 @@ static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsign return ret; } return 0; + case CHUNK_TYPE_SIN_LZ4: + ret = process_sin_lz4_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, + crc_ptr); + if (ret < 0) { + verbose_error(s->verbose, -EINVAL, "lz4 block at %" PRId64, offset); + return ret; + } + return chunk_header->chunk_sz; default: verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type, offset);