summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-01-09 16:06:20 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2018-01-17 11:04:44 +0100
commit13f649a4f960b6ded4735ba99d59dee49e3e77cc (patch)
tree55442403d4fadc618ecb34362f010f8365d5bee1 /lib
parentadb11d1ac28c6d18e413d5dbe1da7dfd72654c61 (diff)
downloadbarebox-13f649a4f960b6ded4735ba99d59dee49e3e77cc.tar.gz
barebox-13f649a4f960b6ded4735ba99d59dee49e3e77cc.tar.xz
Add support for fastboot sparse images
This adds support for reading Android fastboot sparse images. This code is based on the corresponding U-Boot code, but has been heavily modified to provide a read-like API which better fits into barebox. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig3
-rw-r--r--lib/Makefile1
-rw-r--r--lib/image-sparse.c249
3 files changed, 253 insertions, 0 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 9562b1b8c2..637b3f1003 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -79,6 +79,9 @@ config LIBSCAN
config LIBUBIGEN
bool
+config IMAGE_SPARSE
+ bool
+
config STMP_DEVICE
bool
diff --git a/lib/Makefile b/lib/Makefile
index 1be1742499..0d5ac6586c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
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 0000000000..7137d15fd0
--- /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 <config.h>
+#include <common.h>
+#include <image-sparse.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <fs.h>
+#include <libfile.h>
+#include <linux/sizes.h>
+
+#include <linux/math64.h>
+
+#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);
+}