diff --git a/include/image-sparse.h b/include/image-sparse.h new file mode 100644 index 0000000..29242f4 --- /dev/null +++ b/include/image-sparse.h @@ -0,0 +1,67 @@ +/* + * This is from the Android Project, + * Repository: https://android.googlesource.com/platform/system/core + * File: libsparse/sparse_format.h + * Commit: 28fa5bc347390480fe190294c6c385b6a9f0d68b + * Copyright (C) 2010 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _IMAGE_SPARSE_H +#define _IMAGE_SPARSE_H + +struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +}; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +struct chunk_header { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ +}; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ + +static inline int is_sparse_image(const void *buf) +{ + const struct sparse_header *s = buf; + + if ((le32_to_cpu(s->magic) == SPARSE_HEADER_MAGIC) && + (le16_to_cpu(s->major_version) == 1)) + return 1; + + return 0; +} + +struct sparse_image_ctx; + +struct sparse_image_ctx *sparse_image_open(const char *path); +int sparse_image_read(struct sparse_image_ctx *si, void *buf, + loff_t *pos, size_t len, int *retlen); +void sparse_image_close(struct sparse_image_ctx *si); +loff_t sparse_image_size(struct sparse_image_ctx *si); + +#endif /* _IMAGE_SPARSE_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 9562b1b..637b3f1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -79,6 +79,9 @@ config LIBUBIGEN bool +config IMAGE_SPARSE + bool + config STMP_DEVICE bool diff --git a/lib/Makefile b/lib/Makefile index 1be1742..0d5ac65 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -32,6 +32,7 @@ obj-y += glob.o obj-y += notifier.o obj-y += random.o +obj-$(CONFIG_IMAGE_SPARSE) += image-sparse.o obj-y += lzo/ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-y += show_progress.o diff --git a/lib/image-sparse.c b/lib/image-sparse.c new file mode 100644 index 0000000..7137d15 --- /dev/null +++ b/lib/image-sparse.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2009, Google Inc. + * All rights reserved. + * + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. + * Portions Copyright 2014 Broadcom Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: + * Although it is very similar, this license text is not identical + * to the "BSD-3-Clause", therefore, DO NOT MODIFY THIS LICENSE TEXT! + */ +#define pr_fmt(fmt) "image-sparse: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE +#define CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE (1024 * 512) +#endif + +struct sparse_image_ctx { + int fd; + struct sparse_header sparse; + int processed_chunks; + struct chunk_header chunk; + loff_t pos; + size_t remaining; + uint32_t fill_val; +}; + +int sparse_seek(struct sparse_image_ctx *si) +{ + unsigned int chunk_data_sz, payload; + loff_t offs; + int ret; + +again: + if (si->processed_chunks == si->sparse.total_chunks) + return 0; + + /* Read and skip over chunk header */ + ret = read_full(si->fd, &si->chunk, + sizeof(struct chunk_header)); + if (ret < 0) + return ret; + if (ret < sizeof(struct chunk_header)) + return -EINVAL; + + pr_debug("=== Chunk Header ===\n"); + pr_debug("chunk_type: 0x%x\n", si->chunk.chunk_type); + pr_debug("chunk_data_sz: 0x%x\n", si->chunk.chunk_sz); + pr_debug("total_size: 0x%x\n", si->chunk.total_sz); + + if (si->sparse.chunk_hdr_sz > sizeof(struct chunk_header)) { + /* + * Skip the remaining bytes in a header that is longer + * than we expected. + */ + offs = lseek(si->fd, si->sparse.chunk_hdr_sz - + sizeof(struct chunk_header), SEEK_CUR); + if (offs == -1) + return -errno; + } + + chunk_data_sz = si->sparse.blk_sz * si->chunk.chunk_sz; + payload = si->chunk.total_sz - si->sparse.chunk_hdr_sz; + + si->processed_chunks++; + + switch (si->chunk.chunk_type) { + case CHUNK_TYPE_RAW: + if (payload != chunk_data_sz) + return -EINVAL; + + si->remaining = payload; + + break; + + case CHUNK_TYPE_FILL: + if (payload != sizeof(uint32_t)) + return -EINVAL; + + ret = read_full(si->fd, &si->fill_val, sizeof(uint32_t)); + if (ret < 0) + return ret; + if (ret < sizeof(uint32_t)) + return -EINVAL; + + si->remaining = chunk_data_sz; + + break; + + case CHUNK_TYPE_DONT_CARE: + si->pos += chunk_data_sz; + goto again; + + case CHUNK_TYPE_CRC32: + if (payload != sizeof(uint32_t)) + return -EINVAL; + + offs = lseek(si->fd, chunk_data_sz, SEEK_CUR); + if (offs == -1) + return -EINVAL; + goto again; + + default: + pr_err("Unknown chunk type 0x%04x", + si->chunk.chunk_type); + return -EINVAL; + } + + return 1; +} + +loff_t sparse_image_size(struct sparse_image_ctx *si) +{ + return (loff_t)si->sparse.blk_sz * si->sparse.total_blks; +} + +struct sparse_image_ctx *sparse_image_open(const char *path) +{ + struct sparse_image_ctx *si; + loff_t offs; + int ret; + + si = xzalloc(sizeof(*si)); + + si->fd = open(path, O_RDONLY); + if (si->fd < 0) { + ret = -errno; + goto out; + } + + /* Read and skip over sparse image header */ + read(si->fd, &si->sparse, sizeof(struct sparse_header)); + + if (si->sparse.file_hdr_sz > sizeof(struct sparse_header)) { + /* + * Skip the remaining bytes in a header that is longer than + * we expected. + */ + offs = lseek(si->fd, si->sparse.file_hdr_sz, SEEK_SET); + if (offs == -1) { + ret = -errno; + goto out; + } + } + + ret = sparse_seek(si); + if (ret < 0) + goto out; + + return si; +out: + free(si); + + return ERR_PTR(ret); +} + +int sparse_image_read(struct sparse_image_ctx *si, void *buf, loff_t *pos, + size_t len, int *retlen) +{ + size_t now; + int ret, i; + + if (si->remaining == 0) { + ret = sparse_seek(si); + if (ret < 0) + return ret; + if (ret == 0) { + *retlen = 0; + return 0; + } + } + + *pos = si->pos; + + now = min(si->remaining, len); + + switch (si->chunk.chunk_type) { + case CHUNK_TYPE_RAW: + ret = read_full(si->fd, buf, now); + if (ret < 0) + return ret; + if (ret < now) + return -EINVAL; + + break; + + case CHUNK_TYPE_FILL: + if (now & 3) + return -EINVAL; + + for (i = 0; i < now / sizeof(uint32_t); i++) { + uint32_t *buf32 = buf; + + buf32[i] = si->fill_val; + } + + break; + default: + return -EINVAL; + } + + si->pos += now; + si->remaining -= now; + + *retlen = now; + + return 0; +} + +void sparse_image_close(struct sparse_image_ctx *si) +{ + close(si->fd); + free(si); +}