├── .gitignore ├── LICENSE ├── README.md ├── assets ├── patch_1_2.bin ├── patch_2_3.bin ├── v1.bin ├── v2.bin └── v3.bin ├── components ├── delta │ ├── CMakeLists.txt │ ├── LICENSE │ ├── component.mk │ ├── delta.c │ └── include │ │ └── delta.h └── detools │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.rst │ ├── component.mk │ ├── detools.c │ ├── heatshrink │ ├── LICENSE │ ├── README.md │ ├── heatshrink_common.h │ ├── heatshrink_config.h │ ├── heatshrink_decoder.c │ └── heatshrink_decoder.h │ └── include │ └── detools.h └── examples └── http_delta_ota ├── CMakeLists.txt ├── Makefile ├── main ├── CMakeLists.txt ├── component.mk └── main.c ├── partitions.csv └── sdkconfig.defaults /.gitignore: -------------------------------------------------------------------------------- 1 | .config 2 | *.o 3 | *.pyc 4 | 5 | # gtags 6 | GTAGS 7 | GRTAGS 8 | GPATH 9 | 10 | # emacs 11 | .dir-locals.el 12 | 13 | # emacs temp file suffixes 14 | *~ 15 | .#* 16 | \#*# 17 | 18 | # eclipse setting 19 | .settings 20 | 21 | # MacOS directory files 22 | .DS_Store 23 | 24 | # Components Unit Test Apps files 25 | components/**/build 26 | components/**/sdkconfig 27 | components/**/sdkconfig.old 28 | 29 | # Example project files 30 | examples/**/sdkconfig 31 | examples/**/sdkconfig.old 32 | examples/**/build 33 | 34 | # Doc build artifacts 35 | docs/_build/ 36 | docs/doxygen_sqlite3.db 37 | 38 | # Downloaded font files 39 | docs/_static/DejaVuSans.ttf 40 | docs/_static/NotoSansSC-Regular.otf 41 | 42 | # Unit test app files 43 | tools/unit-test-app/sdkconfig 44 | tools/unit-test-app/sdkconfig.old 45 | tools/unit-test-app/build 46 | tools/unit-test-app/builds 47 | tools/unit-test-app/output 48 | tools/unit-test-app/test_configs 49 | 50 | # Unit Test CMake compile log folder 51 | log_ut_cmake 52 | 53 | # test application build files 54 | tools/test_apps/**/build 55 | tools/test_apps/**/sdkconfig 56 | tools/test_apps/**/sdkconfig.old 57 | 58 | # IDF monitor test 59 | tools/test_idf_monitor/outputs 60 | 61 | TEST_LOGS 62 | 63 | # gcov coverage reports 64 | *.gcda 65 | *.gcno 66 | coverage.info 67 | coverage_report/ 68 | 69 | test_multi_heap_host 70 | 71 | # VS Code Settings 72 | .vscode/ 73 | 74 | # VIM files 75 | *.swp 76 | *.swo 77 | 78 | # Clion IDE CMake build & config 79 | .idea/ 80 | cmake-build-*/ 81 | 82 | # Results for the checking of the Python coding style and static analysis 83 | .mypy_cache 84 | flake8_output.txt 85 | 86 | # ESP-IDF default build directory name 87 | build 88 | 89 | # lock files for examples and components 90 | dependencies.lock 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Laukik Hase 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | Note: The 'delta' component is licensed under the Apache-2.0 License. 24 | Please see 'components/delta/LICENSE' for more information. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Status](https://img.shields.io/badge/status-experimental-red) 2 | # ESP32 Compressed Delta OTA Updates 3 | 4 | ## About the Project 5 | 6 | The project aims at enabling firmware update of ESP32 Over-the-Air with compressed delta binaries. Testing was done with ESP32-DevKitC v4 board. 7 | ## Getting Started 8 | 9 | ### Hardware Required 10 | 11 | To run the OTA demo, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC). 12 | You can also try running on ESP32-S2, ESP32-C3 or ESP32-S3 dev boards and let me know how it worked out. 13 | 14 | ### Prerequisites 15 | 16 | * **ESP-IDF v4.3 and above** 17 | 18 | You can visit the [ESP-IDF Programmming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#installation-step-by-step) for the installation steps. 19 | 20 | * **detools v0.49.0 and above** 21 | 22 | Binary delta encoding in Python 3.6+. You can follow the instructions [here](https://pypi.org/project/detools/) for installation. 23 | 24 | ## Usage 25 | 26 | 1. Build the example `examples/http_delta_ota` - `idf.py build`. You can use the binary built here as the `base_binary`. 27 | 28 | For example usage, a `while(1)` loop printing 'Hello World!' was added to this example to create the `updated_binary`. 29 | 30 | 2. To generate the patch, we need 2 application binaries, namely `base_binary` and `updated_binary`. 31 | 32 | `detools create_patch -c heatshrink base_binary.bin updated_binary.bin patch.bin` 33 | 34 | 3. Open the project configuration menu (`idf.py menuconfig`) go to `Example Connection Configuration` -> 35 | 1. WiFi SSID: WiFi network to which your PC is also connected to. 36 | 2. WiFi Password: WiFi password 37 | 38 | 4. In order to test the OTA demo -> `examples/http_delta_ota` : 39 | 1. Flash the firmware `idf.py -p PORT -b BAUD flash`. 40 | 2. Run `idf.py -p PORT monitor` and note down the IP assigned to your ESP module. The default port is 80. 41 | 42 | 5. After getting the IP address, send the patch binary through a HTTP Post request over cURL. 43 | 44 | `curl -v -X POST --data-binary @- < patch.bin 192.168.201.9:80/ota` 45 | 46 | ## Demo Results 47 | 48 | - Base binary: `assets/v1.bin` 49 | - Updated binary: `assets/v2.bin` 50 | - Patch binary: `assets/patch_1_2.bin` 51 | - After successfully patching and rebooting, you can see the `Hello World` logs. 52 | 53 | https://user-images.githubusercontent.com/42297532/152674614-2f4d3b9d-08d2-45bc-a3b6-29f33a191fd7.mp4 54 | 55 | ## Experiments 56 | 57 | | Chip | Scenario | Base binary | Updated binary | Compressed binary patch (Heatshrink) | Patch-to-File % | 58 | |-------|----------------------------------------------|-------------|----------------|--------------------------------------|-----------------| 59 | | ESP32 | test_basic_enable_small_feature | 168208 | 155136 | 11036 | 7.11% | 60 | | ESP32 | test_nvs_app_modification | 190656 | 199824 | 16245 | 8.13% | 61 | | ESP32 | test_http_upgrade_with_ssl | 672736 | 761184 | 138839 | 18.24% | 62 | | ESP32 | test_provisioning_upgrade_idf_patch_version | 966656 | 924448 | 234096 | 25.32% | 63 | 64 | - As Heatshrink uses static allocation with small look-ahead buffers, it has almost no impact on heap memory. 65 | ### Test Scenarios: ESP-IDF 4.4-dev (Master branch) 66 | 67 | 1. **test_basic_enable_small_feature:** Enabling a small feature in an update 68 | 69 | Base binary: Compile `get-started/hello-world` example 70 | 71 | Updated binary: Same, but disable CONFIG_VFS_SUPPORT_IO option in sdkconfig 72 | 73 | 2. **test_nvs_app_modification**: Changing user application flow without changing the set of libraries used 74 | 75 | Base binary: Compile `storage/nvs_rw_value` example 76 | 77 | Updated binary: Compile `storage/nvs_rw_blob` example 78 | 79 | 3. **test_http_upgrade_with_ssl**: Changing user application flow with new set of libraries added 80 | 81 | Base binary: Compile `protocols/http_server/simple` example 82 | 83 | Updated binary: Compile `protocols/https_server/simple` example 84 | 85 | 4. **test_provisioning_upgrade_idf_patch_version**: Upgrading IDF to the next patch version 86 | 87 | Base binary: Using IDF 4.3, compile `provisioning/wifi_prov_mgr` example 88 | 89 | Update binary: Same, but with IDF 4.3.1; sdkconfig is generated from scratch. 90 | 91 | ## To-do: 92 | 93 | - [x] Experiments with more test scenarios, especially with bigger binaries 94 | 95 | - [x] Add complete workflow example 96 | - Currently patch is applied to the `ota_0` partition rather than the `factory` 97 | 98 | - [ ] Optimize Heatshrink compression parameters 99 | - Currently running on low memory usage mode (8, 7) and static allocation 100 | - Memory usage and app binary size analysis 101 | 102 | - [ ] Binary patch with LZMA compression in detools 103 | - LZMA is memory-heavy but will provide a greater compression ratio for the patch 104 | 105 | ## Acknowledgements & Resources 106 | 107 | - detool: Binary delta encoding in Python 3 and C 🡒 [Source](https://github.com/eerimoq/detools) | [Docs](https://detools.readthedocs.io/en/latest/) 108 | - heatshrink: An Embedded Data Compression Library 🡒 [Source](https://github.com/atomicobject/heatshrink) | [Blog](https://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/) 109 | - Delta updates for embedded systems 🡒 [Source](https://gitlab.endian.se/thesis-projects/delta-updates-for-embedded-systems) | [Docs](https://odr.chalmers.se/bitstream/20.500.12380/302598/1/21-17%20Lindh.pdf) 110 | - bspatch for ESP32 🡒 [Source](https://github.com/Blockstream/esp32_bsdiff) 111 | 112 | ## License 113 | 114 | Distributed under the MIT License. See `LICENSE` for more information. 115 | 116 | **Note**: The `delta` component is licensed under the Apache-2.0 License. Please see `components/delta/LICENSE` for more information. 117 | -------------------------------------------------------------------------------- /assets/patch_1_2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/assets/patch_1_2.bin -------------------------------------------------------------------------------- /assets/patch_2_3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/assets/patch_2_3.bin -------------------------------------------------------------------------------- /assets/v1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/assets/v1.bin -------------------------------------------------------------------------------- /assets/v2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/assets/v2.bin -------------------------------------------------------------------------------- /assets/v3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/assets/v3.bin -------------------------------------------------------------------------------- /components/delta/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "delta.c" 2 | INCLUDE_DIRS "include" 3 | PRIV_REQUIRES app_update detools freertos log spi_flash) -------------------------------------------------------------------------------- /components/delta/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Thesis projects 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /components/delta/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/components/delta/component.mk -------------------------------------------------------------------------------- /components/delta/delta.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2016 Intel Corporation 3 | * 2020 Thesis projects 4 | * 5 | * SPDX-License-Identifier: Apache 2.0 License 6 | * 7 | * SPDX-FileContributor: 2021 Laukik Hase 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "freertos/FreeRTOS.h" 15 | #include "freertos/task.h" 16 | 17 | #include "esp_err.h" 18 | #include "esp_log.h" 19 | #include "esp_system.h" 20 | 21 | #include "esp_partition.h" 22 | #include "esp_ota_ops.h" 23 | 24 | #include "detools.h" 25 | #include "delta.h" 26 | 27 | static const char *TAG = "delta"; 28 | 29 | typedef struct flash_mem { 30 | const esp_partition_t *src; 31 | const esp_partition_t *dest; 32 | const esp_partition_t *patch; 33 | size_t src_offset; 34 | size_t patch_offset; 35 | esp_ota_handle_t ota_handle; 36 | } flash_mem_t; 37 | 38 | static int delta_flash_write_dest(void *arg_p, const uint8_t *buf_p, size_t size) 39 | { 40 | flash_mem_t *flash; 41 | flash = (flash_mem_t *)arg_p; 42 | 43 | if (!flash) { 44 | return -DELTA_CASTING_ERROR; 45 | } 46 | if (size <= 0) { 47 | return -DELTA_INVALID_BUF_SIZE; 48 | } 49 | 50 | if (esp_ota_write(flash->ota_handle, buf_p, size) != ESP_OK) { 51 | return -DELTA_WRITING_ERROR; 52 | } 53 | 54 | return DELTA_OK; 55 | } 56 | 57 | static int delta_flash_read_src(void *arg_p, uint8_t *buf_p, size_t size) 58 | { 59 | flash_mem_t *flash; 60 | flash = (flash_mem_t *)arg_p; 61 | 62 | if (!flash) { 63 | return -DELTA_CASTING_ERROR; 64 | } 65 | if (size <= 0) { 66 | return -DELTA_INVALID_BUF_SIZE; 67 | } 68 | 69 | if (esp_partition_read(flash->src, flash->src_offset, buf_p, size) != ESP_OK) { 70 | return -DELTA_READING_SOURCE_ERROR; 71 | } 72 | 73 | flash->src_offset += size; 74 | if (flash->src_offset >= flash->src->size) { 75 | return -DELTA_OUT_OF_MEMORY; 76 | } 77 | 78 | return DELTA_OK; 79 | } 80 | 81 | static int delta_flash_read_patch(void *arg_p, uint8_t *buf_p, size_t size) 82 | { 83 | flash_mem_t *flash; 84 | flash = (flash_mem_t *)arg_p; 85 | 86 | if (!flash) { 87 | return -DELTA_CASTING_ERROR; 88 | } 89 | if (size <= 0) { 90 | return -DELTA_INVALID_BUF_SIZE; 91 | } 92 | 93 | if (esp_partition_read(flash->patch, flash->patch_offset, buf_p, size) != ESP_OK) { 94 | return -DELTA_READING_PATCH_ERROR; 95 | } 96 | 97 | flash->patch_offset += size; 98 | if (flash->patch_offset >= flash->patch->size) { 99 | return -DELTA_READING_PATCH_ERROR; 100 | } 101 | 102 | return DELTA_OK; 103 | } 104 | 105 | static int delta_flash_seek_src(void *arg_p, int offset) 106 | { 107 | flash_mem_t *flash; 108 | flash = (flash_mem_t *)arg_p; 109 | 110 | if (!flash) { 111 | return -DELTA_CASTING_ERROR; 112 | } 113 | 114 | flash->src_offset += offset; 115 | if (flash->src_offset >= flash->src->size) { 116 | return -DELTA_SEEKING_ERROR; 117 | } 118 | 119 | return DELTA_OK; 120 | } 121 | 122 | static int delta_init_flash_mem(flash_mem_t *flash, const delta_opts_t *opts) 123 | { 124 | if (!flash) { 125 | return -DELTA_PARTITION_ERROR; 126 | } 127 | 128 | flash->src = esp_ota_get_running_partition(); 129 | flash->dest = esp_ota_get_next_update_partition(NULL); 130 | flash->patch = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, opts->patch); 131 | 132 | if (flash->src == NULL || flash->dest == NULL || flash->patch == NULL) { 133 | return -DELTA_PARTITION_ERROR; 134 | } 135 | 136 | if (flash->src->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MAX || 137 | flash->dest->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MAX) { 138 | return -DELTA_PARTITION_ERROR; 139 | } 140 | 141 | if (esp_ota_begin(flash->dest, OTA_SIZE_UNKNOWN, &(flash->ota_handle)) != ESP_OK) { 142 | return -DELTA_PARTITION_ERROR; 143 | } 144 | esp_log_level_set("esp_image", ESP_LOG_ERROR); 145 | 146 | flash->src_offset = 0; 147 | flash->patch_offset = 0; 148 | 149 | return DELTA_OK; 150 | } 151 | 152 | static int delta_set_boot_partition(flash_mem_t *flash) 153 | { 154 | if (esp_ota_set_boot_partition(flash->dest) != ESP_OK) { 155 | return -DELTA_TARGET_IMAGE_ERROR; 156 | } 157 | free(flash); 158 | 159 | const esp_partition_t *boot_partition = esp_ota_get_boot_partition(); 160 | ESP_LOGI(TAG, "Next Boot Partition: Subtype %d at Offset 0x%x", boot_partition->subtype, boot_partition->address); 161 | ESP_LOGI(TAG, "Ready to reboot!!!"); 162 | 163 | return DELTA_OK; 164 | } 165 | 166 | int delta_partition_init(delta_partition_writer_t *writer, const char *partition, int patch_size) 167 | { 168 | if (writer == NULL || partition == NULL) { 169 | return -DELTA_INVALID_ARGUMENT_ERROR; 170 | } 171 | 172 | const esp_partition_t *patch = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 173 | ESP_PARTITION_SUBTYPE_DATA_SPIFFS, partition); 174 | if (patch == NULL) { 175 | ESP_LOGE(TAG, "Partition Error: Could not find '%s' partition", partition); 176 | return ESP_FAIL; 177 | } 178 | 179 | size_t patch_page_size = (patch_size + PARTITION_PAGE_SIZE) - (patch_size % PARTITION_PAGE_SIZE); 180 | if (esp_partition_erase_range(patch, 0, patch_page_size) != ESP_OK) { 181 | ESP_LOGE(TAG, "Partition Error: Could not erase '%s' region!", partition); 182 | return ESP_FAIL; 183 | } 184 | 185 | writer->name = partition; 186 | writer->patch = patch; 187 | writer->size = patch_size; 188 | writer->offset = 0; 189 | 190 | return ESP_OK; 191 | } 192 | 193 | int delta_partition_write(delta_partition_writer_t *writer, const char *buf, int size) 194 | { 195 | if (writer == NULL || buf == NULL) { 196 | return -DELTA_INVALID_ARGUMENT_ERROR; 197 | } 198 | 199 | if (writer->offset >= writer->size) { 200 | return -DELTA_OUT_OF_BOUNDS_ERROR; 201 | } 202 | 203 | if (esp_partition_write(writer->patch, writer->offset, buf, size) != ESP_OK) { 204 | ESP_LOGE(TAG, "Partition Error: Could not write to '%s' region!", writer->name); 205 | return ESP_FAIL; 206 | }; 207 | 208 | writer->offset += size; 209 | return ESP_OK; 210 | } 211 | 212 | int delta_check_and_apply(int patch_size, const delta_opts_t *opts) 213 | { 214 | static const delta_opts_t DEFAULT_DELTA_OPTS = { 215 | .src = DEFAULT_PARTITION_LABEL_SRC, 216 | .dest = DEFAULT_PARTITION_LABEL_DEST, 217 | .patch = DEFAULT_PARTITION_LABEL_PATCH 218 | }; 219 | 220 | ESP_LOGI(TAG, "Initializing delta update..."); 221 | 222 | flash_mem_t *flash = NULL; 223 | int ret = 0; 224 | 225 | if (patch_size < 0) { 226 | return patch_size; 227 | } else if (patch_size > 0) { 228 | flash = calloc(1, sizeof(flash_mem_t)); 229 | if (!flash) { 230 | return -DELTA_OUT_OF_MEMORY; 231 | } 232 | 233 | if (!opts) { 234 | opts = &DEFAULT_DELTA_OPTS; 235 | } 236 | 237 | ret = delta_init_flash_mem(flash, opts); 238 | if (ret) { 239 | return ret; 240 | } 241 | 242 | ret = detools_apply_patch_callbacks(delta_flash_read_src, 243 | delta_flash_seek_src, 244 | delta_flash_read_patch, 245 | (size_t) patch_size, 246 | delta_flash_write_dest, 247 | flash); 248 | 249 | if (ret <= 0) { 250 | return ret; 251 | } 252 | 253 | ESP_LOGI(TAG, "Patch Successful!!!"); 254 | return delta_set_boot_partition(flash); 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | const char *delta_error_as_string(int error) 261 | { 262 | if (error < 28) { 263 | return detools_error_as_string(error); 264 | } 265 | 266 | if (error < 0) { 267 | error *= -1; 268 | } 269 | 270 | switch (error) { 271 | case DELTA_OUT_OF_MEMORY: 272 | return "Target partition out of memory."; 273 | case DELTA_READING_PATCH_ERROR: 274 | return "Error reading patch binary."; 275 | case DELTA_READING_SOURCE_ERROR: 276 | return "Error reading source image."; 277 | case DELTA_WRITING_ERROR: 278 | return "Error writing to target image."; 279 | case DELTA_SEEKING_ERROR: 280 | return "Seek error: source image."; 281 | case DELTA_CASTING_ERROR: 282 | return "Error casting to flash_mem_t."; 283 | case DELTA_INVALID_BUF_SIZE: 284 | return "Read/write buffer less or equal to 0."; 285 | case DELTA_CLEARING_ERROR: 286 | return "Could not erase target region."; 287 | case DELTA_PARTITION_ERROR: 288 | return "Flash partition not found."; 289 | case DELTA_TARGET_IMAGE_ERROR: 290 | return "Invalid target image to boot from."; 291 | default: 292 | return "Unknown error."; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /components/delta/include/delta.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2016 Intel Corporation 3 | * 2020 Thesis projects 4 | * 5 | * SPDX-License-Identifier: Apache 2.0 License 6 | * 7 | * SPDX-FileContributor: 2021 Laukik Hase 8 | */ 9 | 10 | #pragma once 11 | 12 | /* PARTITION LABELS */ 13 | #define DEFAULT_PARTITION_LABEL_SRC "factory" 14 | #define DEFAULT_PARTITION_LABEL_DEST "ota_0" 15 | #define DEFAULT_PARTITION_LABEL_PATCH "patch" 16 | 17 | /* PAGE SIZE */ 18 | #define PARTITION_PAGE_SIZE (0x1000) 19 | 20 | /* Error codes. */ 21 | #define DELTA_OK 0 22 | #define DELTA_OUT_OF_MEMORY 28 23 | #define DELTA_READING_PATCH_ERROR 29 24 | #define DELTA_READING_SOURCE_ERROR 30 25 | #define DELTA_WRITING_ERROR 31 26 | #define DELTA_SEEKING_ERROR 32 27 | #define DELTA_CASTING_ERROR 33 28 | #define DELTA_INVALID_BUF_SIZE 34 29 | #define DELTA_CLEARING_ERROR 35 30 | #define DELTA_PARTITION_ERROR 36 31 | #define DELTA_TARGET_IMAGE_ERROR 37 32 | #define DELTA_INVALID_ARGUMENT_ERROR 38 33 | #define DELTA_OUT_OF_BOUNDS_ERROR 39 34 | 35 | typedef struct { 36 | const char *src; 37 | const char *dest; 38 | const char *patch; 39 | } delta_opts_t; 40 | 41 | #define INIT_DEFAULT_DELTA_OPTS() { \ 42 | .src = DEFAULT_PARTITION_LABEL_SRC, \ 43 | .dest = DEFAULT_PARTITION_LABEL_DEST, \ 44 | .patch = DEFAULT_PARTITION_LABEL_PATCH \ 45 | } 46 | 47 | typedef struct { 48 | const char *name; 49 | const void *patch; 50 | int offset; 51 | int size; 52 | } delta_partition_writer_t; 53 | 54 | int delta_partition_init(delta_partition_writer_t *writer, const char *partition, int patch_size); 55 | 56 | int delta_partition_write(delta_partition_writer_t *writer, const char *buf, int size); 57 | 58 | /** 59 | * Checks if there is patch in the patch partition 60 | * and applies that patch if it exists. Then restarts 61 | * the device and boots from the new image. 62 | * 63 | * @param[in] patch_size size of the patch. 64 | * @param[in] opts options for applying the patch. 65 | * 66 | * @return zero(0) if no patch or a negative error 67 | * code. 68 | */ 69 | int delta_check_and_apply(int patch_size, const delta_opts_t *opts); 70 | 71 | /** 72 | * Get the error string for given error code. 73 | * 74 | * @param[in] Error code. 75 | * 76 | * @return Error string. 77 | */ 78 | const char *delta_error_as_string(int error); 79 | -------------------------------------------------------------------------------- /components/detools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "detools.c" "heatshrink/heatshrink_decoder.c" 2 | INCLUDE_DIRS "include" "heatshrink") 3 | -------------------------------------------------------------------------------- /components/detools/LICENSE: -------------------------------------------------------------------------------- 1 | bsdiff/sais.c based on sais-lite 2.4.1 : 2 | Copyright (c) 2008-2010 Yuta Mori. (MIT License) 3 | 4 | Other files: 5 | 6 | BSD 2-Clause License 7 | 8 | Copyright 2003-2005, Colin Percival (Original C implementation) 9 | Copyright (c) 2019, Erik Moqvist 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright notice, this 16 | list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /components/detools/README.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | An implementation of detools in the C programming language. 5 | 6 | Features: 7 | 8 | - Incremental apply of `sequential`_ and `in-place`_ patches. 9 | 10 | - bsdiff algorithm. 11 | 12 | - LZMA, `heatshrink`_ or CRLE compression. 13 | 14 | - Dump (and store) the apply patch state at any time. Restore it 15 | later, possibly after a system reboot or program crash. Only 16 | `heatshrink`_ and CRLE compressions are currently supported. 17 | 18 | Goals: 19 | 20 | - Easy to use. 21 | 22 | - Low RAM usage. 23 | 24 | - Small code size. 25 | 26 | - Portable. 27 | 28 | .. _heatshrink: https://github.com/atomicobject/heatshrink 29 | 30 | .. _sequential: https://detools.readthedocs.io/en/latest/#id1 31 | 32 | .. _in-place: https://detools.readthedocs.io/en/latest/#id3 33 | 34 | .. _detools.h: https://github.com/eerimoq/detools/blob/master/src/c/detools.h 35 | 36 | .. _examples folder: https://github.com/eerimoq/detools/tree/master/src/c/examples 37 | -------------------------------------------------------------------------------- /components/detools/component.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ESP32-Musings/esp32_compressed_delta_ota_update/aea1a38bd6f9372501303af4f257cf9bae76a5fe/components/detools/component.mk -------------------------------------------------------------------------------- /components/detools/detools.c: -------------------------------------------------------------------------------- 1 | /** 2 | * BSD 2-Clause License 3 | * 4 | * Copyright (c) 2019-2020, Erik Moqvist 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include "detools.h" 35 | 36 | /* Patch types. */ 37 | #define PATCH_TYPE_SEQUENTIAL 0 38 | #define PATCH_TYPE_IN_PLACE 1 39 | 40 | /* Compressions. */ 41 | #define COMPRESSION_NONE 0 42 | #define COMPRESSION_LZMA 1 43 | #define COMPRESSION_CRLE 2 44 | #define COMPRESSION_HEATSHRINK 4 45 | 46 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 47 | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 48 | #define DIV_CEIL(n, d) (((n) + (d) - 1) / (d)) 49 | 50 | /* 51 | * Utility functions. 52 | */ 53 | 54 | static size_t chunk_left(struct detools_apply_patch_chunk_t *self_p) 55 | { 56 | return (self_p->size - self_p->offset); 57 | } 58 | 59 | static bool chunk_available(struct detools_apply_patch_chunk_t *self_p) 60 | { 61 | return (chunk_left(self_p) > 0); 62 | } 63 | 64 | static uint8_t chunk_get_no_check(struct detools_apply_patch_chunk_t *self_p) 65 | { 66 | uint8_t data; 67 | 68 | data = self_p->buf_p[self_p->offset]; 69 | self_p->offset++; 70 | 71 | return (data); 72 | } 73 | 74 | static int chunk_get(struct detools_apply_patch_chunk_t *self_p, 75 | uint8_t *data_p) 76 | { 77 | if (!chunk_available(self_p)) { 78 | return (1); 79 | } 80 | 81 | *data_p = chunk_get_no_check(self_p); 82 | 83 | return (0); 84 | } 85 | 86 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 \ 87 | || DETOOLS_CONFIG_COMPRESSION_CRLE == 1 \ 88 | || DETOOLS_CONFIG_COMPRESSION_LZMA == 1 89 | 90 | static void chunk_read_all_no_check(struct detools_apply_patch_chunk_t *self_p, 91 | uint8_t *buf_p, 92 | size_t size) 93 | { 94 | memcpy(buf_p, &self_p->buf_p[self_p->offset], size); 95 | self_p->offset += size; 96 | } 97 | 98 | #endif 99 | 100 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 \ 101 | || DETOOLS_CONFIG_COMPRESSION_CRLE == 1 102 | 103 | static int chunk_read(struct detools_apply_patch_chunk_t *self_p, 104 | uint8_t *buf_p, 105 | size_t *size_p) 106 | { 107 | if (!chunk_available(self_p)) { 108 | return (1); 109 | } 110 | 111 | *size_p = MIN(*size_p, chunk_left(self_p)); 112 | chunk_read_all_no_check(self_p, buf_p, *size_p); 113 | 114 | return (0); 115 | } 116 | 117 | #endif 118 | 119 | static bool is_overflow(int value) 120 | { 121 | return ((value + 7) > (int)(8 * sizeof(int))); 122 | } 123 | 124 | static int chunk_unpack_header_size(struct detools_apply_patch_chunk_t *self_p, 125 | int *size_p) 126 | { 127 | uint8_t byte; 128 | int offset; 129 | int res; 130 | 131 | res = chunk_get(self_p, &byte); 132 | 133 | if (res != 0) { 134 | return (-DETOOLS_SHORT_HEADER); 135 | } 136 | 137 | *size_p = (byte & 0x3f); 138 | offset = 6; 139 | 140 | while ((byte & 0x80) != 0) { 141 | res = chunk_get(self_p, &byte); 142 | 143 | if (res != 0) { 144 | return (-DETOOLS_SHORT_HEADER); 145 | } 146 | 147 | if (is_overflow(offset)) { 148 | return (-DETOOLS_CORRUPT_PATCH_OVERFLOW); 149 | } 150 | 151 | *size_p |= ((byte & 0x7f) << offset); 152 | offset += 7; 153 | } 154 | 155 | return (0); 156 | } 157 | 158 | /* 159 | * None patch reader. 160 | */ 161 | 162 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 163 | 164 | static int patch_reader_none_decompress( 165 | struct detools_apply_patch_patch_reader_t *self_p, 166 | uint8_t *buf_p, 167 | size_t *size_p) 168 | { 169 | int res; 170 | struct detools_apply_patch_patch_reader_none_t *none_p; 171 | 172 | none_p = &self_p->compression.none; 173 | 174 | if (none_p->patch_offset + *size_p > none_p->patch_size) { 175 | return (-DETOOLS_CORRUPT_PATCH); 176 | } 177 | 178 | res = chunk_read(self_p->patch_chunk_p, 179 | buf_p, 180 | size_p); 181 | 182 | if (res != 0) { 183 | return (res); 184 | } 185 | 186 | none_p->patch_offset += *size_p; 187 | 188 | return (0); 189 | } 190 | 191 | static int patch_reader_none_destroy( 192 | struct detools_apply_patch_patch_reader_t *self_p) 193 | { 194 | struct detools_apply_patch_patch_reader_none_t *none_p; 195 | 196 | none_p = &self_p->compression.none; 197 | 198 | if (none_p->patch_offset == none_p->patch_size) { 199 | return (0); 200 | } else { 201 | return (-DETOOLS_CORRUPT_PATCH); 202 | } 203 | } 204 | 205 | static int patch_reader_none_init(struct detools_apply_patch_patch_reader_t *self_p, 206 | size_t patch_size) 207 | { 208 | struct detools_apply_patch_patch_reader_none_t *none_p; 209 | 210 | none_p = &self_p->compression.none; 211 | none_p->patch_size = patch_size; 212 | none_p->patch_offset = 0; 213 | self_p->destroy = patch_reader_none_destroy; 214 | self_p->decompress = patch_reader_none_decompress; 215 | 216 | return (0); 217 | } 218 | 219 | #endif 220 | 221 | /* 222 | * Heatshrink patch reader. 223 | */ 224 | 225 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 226 | 227 | static void unpack_heatshrink_header(uint8_t byte, 228 | int8_t *window_sz2_p, 229 | int8_t *lookahead_sz2_p) 230 | { 231 | *window_sz2_p = (((byte >> 4) & 0xf) + 4); 232 | *lookahead_sz2_p = ((byte & 0xf) + 3); 233 | } 234 | 235 | static int patch_reader_heatshrink_decompress( 236 | struct detools_apply_patch_patch_reader_t *self_p, 237 | uint8_t *buf_p, 238 | size_t *size_p) 239 | { 240 | int res; 241 | struct detools_apply_patch_patch_reader_heatshrink_t *heatshrink_p; 242 | size_t size; 243 | size_t left; 244 | HSD_poll_res pres; 245 | HSD_sink_res sres; 246 | uint8_t byte; 247 | 248 | heatshrink_p = &self_p->compression.heatshrink; 249 | left = *size_p; 250 | 251 | if (heatshrink_p->window_sz2 == -1) { 252 | res = chunk_get(self_p->patch_chunk_p, &byte); 253 | 254 | if (res != 0) { 255 | return (1); 256 | } 257 | 258 | unpack_heatshrink_header(byte, 259 | &heatshrink_p->window_sz2, 260 | &heatshrink_p->lookahead_sz2); 261 | 262 | if ((heatshrink_p->window_sz2 != HEATSHRINK_STATIC_WINDOW_BITS) 263 | || (heatshrink_p->lookahead_sz2 != HEATSHRINK_STATIC_LOOKAHEAD_BITS)) { 264 | return (-DETOOLS_HEATSHRINK_HEADER); 265 | } 266 | } 267 | 268 | while (1) { 269 | /* Get available data. */ 270 | pres = heatshrink_decoder_poll(&heatshrink_p->decoder, 271 | buf_p, 272 | left, 273 | &size); 274 | 275 | if (pres < 0) { 276 | return (-DETOOLS_HEATSHRINK_POLL); 277 | } 278 | 279 | buf_p += size; 280 | left -= size; 281 | 282 | if (left == 0) { 283 | return (0); 284 | } 285 | 286 | /* Input (sink) more data if available. */ 287 | res = chunk_get(self_p->patch_chunk_p, &byte); 288 | 289 | if (res == 0) { 290 | sres = heatshrink_decoder_sink(&heatshrink_p->decoder, 291 | &byte, 292 | sizeof(byte), 293 | &size); 294 | 295 | if ((sres < 0) || (size != sizeof(byte))) { 296 | return (-DETOOLS_HEATSHRINK_SINK); 297 | } 298 | } else { 299 | if (left != *size_p) { 300 | *size_p -= left; 301 | 302 | return (0); 303 | } else { 304 | return (1); 305 | } 306 | } 307 | } 308 | 309 | return (res); 310 | } 311 | 312 | static int patch_reader_heatshrink_destroy( 313 | struct detools_apply_patch_patch_reader_t *self_p) 314 | { 315 | struct detools_apply_patch_patch_reader_heatshrink_t *heatshrink_p; 316 | HSD_finish_res fres; 317 | 318 | heatshrink_p = &self_p->compression.heatshrink; 319 | 320 | fres = heatshrink_decoder_finish(&heatshrink_p->decoder); 321 | 322 | if (fres == HSDR_FINISH_DONE) { 323 | return (0); 324 | } else { 325 | return (-DETOOLS_CORRUPT_PATCH); 326 | } 327 | } 328 | 329 | static int patch_reader_heatshrink_init( 330 | struct detools_apply_patch_patch_reader_t *self_p) 331 | { 332 | struct detools_apply_patch_patch_reader_heatshrink_t *heatshrink_p; 333 | 334 | heatshrink_p = &self_p->compression.heatshrink; 335 | heatshrink_p->window_sz2 = -1; 336 | heatshrink_p->lookahead_sz2 = -1; 337 | heatshrink_decoder_reset(&heatshrink_p->decoder); 338 | self_p->destroy = patch_reader_heatshrink_destroy; 339 | self_p->decompress = patch_reader_heatshrink_decompress; 340 | 341 | return (0); 342 | } 343 | 344 | #endif 345 | 346 | /* 347 | * LZMA patch reader. 348 | */ 349 | 350 | #if DETOOLS_CONFIG_COMPRESSION_LZMA == 1 351 | 352 | static int get_decompressed_data( 353 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p, 354 | uint8_t *buf_p, 355 | size_t size) 356 | { 357 | int res; 358 | 359 | if (lzma_p->output_size >= size) { 360 | memcpy(buf_p, lzma_p->output_p, size); 361 | memmove(lzma_p->output_p, 362 | &lzma_p->output_p[size], 363 | lzma_p->output_size - size); 364 | lzma_p->output_size -= size; 365 | res = 0; 366 | } else { 367 | res = 1; 368 | } 369 | 370 | return (res); 371 | } 372 | 373 | static int prepare_input_buffer(struct detools_apply_patch_patch_reader_t *self_p) 374 | { 375 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p; 376 | uint8_t *next_p; 377 | size_t left; 378 | 379 | lzma_p = &self_p->compression.lzma; 380 | left = chunk_left(self_p->patch_chunk_p); 381 | 382 | if (left == 0) { 383 | return (1); 384 | } 385 | 386 | next_p = malloc(lzma_p->stream.avail_in + left); 387 | 388 | if (next_p == NULL) { 389 | return (-DETOOLS_OUT_OF_MEMORY); 390 | } 391 | 392 | if (lzma_p->stream.next_in != NULL) { 393 | memcpy(next_p, lzma_p->stream.next_in, lzma_p->stream.avail_in); 394 | free(lzma_p->input_p); 395 | } 396 | 397 | lzma_p->input_p = next_p; 398 | chunk_read_all_no_check(self_p->patch_chunk_p, 399 | &lzma_p->input_p[lzma_p->stream.avail_in], 400 | left); 401 | lzma_p->stream.next_in = next_p; 402 | lzma_p->stream.avail_in += left; 403 | 404 | return (0); 405 | } 406 | 407 | static int prepare_output_buffer(struct detools_apply_patch_patch_reader_t *self_p, 408 | size_t size) 409 | { 410 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p; 411 | uint8_t *output_p; 412 | 413 | lzma_p = &self_p->compression.lzma; 414 | 415 | output_p = malloc(size); 416 | 417 | if (output_p == NULL) { 418 | return (-DETOOLS_OUT_OF_MEMORY); 419 | } 420 | 421 | if (lzma_p->output_p != NULL) { 422 | memcpy(output_p, lzma_p->output_p, lzma_p->output_size); 423 | free(lzma_p->output_p); 424 | } 425 | 426 | lzma_p->output_p = output_p; 427 | lzma_p->stream.next_out = (output_p + lzma_p->output_size); 428 | lzma_p->stream.avail_out = (size - lzma_p->output_size); 429 | 430 | return (0); 431 | } 432 | 433 | static int patch_reader_lzma_decompress( 434 | struct detools_apply_patch_patch_reader_t *self_p, 435 | uint8_t *buf_p, 436 | size_t *size_p) 437 | { 438 | int res; 439 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p; 440 | lzma_ret ret; 441 | 442 | lzma_p = &self_p->compression.lzma; 443 | 444 | /* Check if enough decompressed data is available. */ 445 | res = get_decompressed_data(lzma_p, buf_p, *size_p); 446 | 447 | if (res == 0) { 448 | return (res); 449 | } 450 | 451 | while (1) { 452 | /* Try to decompress requested data. */ 453 | if (lzma_p->stream.avail_in > 0) { 454 | res = prepare_output_buffer(self_p, *size_p); 455 | 456 | if (res != 0) { 457 | return (res); 458 | } 459 | 460 | ret = lzma_code(&lzma_p->stream, LZMA_RUN); 461 | 462 | switch (ret) { 463 | 464 | case LZMA_OK: 465 | case LZMA_STREAM_END: 466 | break; 467 | 468 | default: 469 | return (-DETOOLS_LZMA_DECODE); 470 | } 471 | 472 | lzma_p->output_size = (size_t)(lzma_p->stream.next_out - lzma_p->output_p); 473 | } 474 | 475 | /* Check if enough decompressed data is available. */ 476 | res = get_decompressed_data(lzma_p, buf_p, *size_p); 477 | 478 | if (res == 0) { 479 | return (res); 480 | } 481 | 482 | /* Get more data to decompress. */ 483 | res = prepare_input_buffer(self_p); 484 | 485 | if (res != 0) { 486 | return (res); 487 | } 488 | } 489 | } 490 | 491 | static int patch_reader_lzma_destroy( 492 | struct detools_apply_patch_patch_reader_t *self_p) 493 | { 494 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p; 495 | 496 | lzma_p = &self_p->compression.lzma; 497 | 498 | if (lzma_p->input_p != NULL) { 499 | free(lzma_p->input_p); 500 | } 501 | 502 | if (lzma_p->output_p != NULL) { 503 | free(lzma_p->output_p); 504 | } 505 | 506 | lzma_end(&lzma_p->stream); 507 | 508 | if ((lzma_p->stream.avail_in == 0) && (lzma_p->output_size == 0)) { 509 | return (0); 510 | } else { 511 | return (-DETOOLS_CORRUPT_PATCH); 512 | } 513 | } 514 | 515 | static int patch_reader_lzma_init(struct detools_apply_patch_patch_reader_t *self_p) 516 | { 517 | lzma_ret ret; 518 | struct detools_apply_patch_patch_reader_lzma_t *lzma_p; 519 | 520 | lzma_p = &self_p->compression.lzma; 521 | memset(&lzma_p->stream, 0, sizeof(lzma_p->stream)); 522 | 523 | ret = lzma_alone_decoder(&lzma_p->stream, UINT64_MAX); 524 | 525 | if (ret != LZMA_OK) { 526 | return (-DETOOLS_LZMA_INIT); 527 | } 528 | 529 | lzma_p->input_p = NULL; 530 | lzma_p->output_p = NULL; 531 | lzma_p->output_size = 0; 532 | self_p->destroy = patch_reader_lzma_destroy; 533 | self_p->decompress = patch_reader_lzma_decompress; 534 | 535 | return (0); 536 | } 537 | 538 | #endif 539 | 540 | /* 541 | * CRLE patch reader. 542 | */ 543 | 544 | #if DETOOLS_CONFIG_COMPRESSION_CRLE == 1 545 | 546 | static void unpack_usize_init(struct detools_unpack_usize_t *self_p) 547 | { 548 | self_p->state = detools_unpack_usize_state_first_t; 549 | self_p->value = 0; 550 | self_p->offset = 0; 551 | } 552 | 553 | static int unpack_usize(struct detools_unpack_usize_t *self_p, 554 | struct detools_apply_patch_chunk_t *patch_chunk_p, 555 | int *size_p) 556 | { 557 | int res; 558 | uint8_t byte; 559 | 560 | switch (self_p->state) { 561 | 562 | case detools_unpack_usize_state_first_t: 563 | self_p->value = 0; 564 | self_p->offset = 0; 565 | self_p->state = detools_unpack_usize_state_consecutive_t; 566 | break; 567 | 568 | case detools_unpack_usize_state_consecutive_t: 569 | break; 570 | 571 | default: 572 | return (-DETOOLS_INTERNAL_ERROR); 573 | } 574 | 575 | do { 576 | res = chunk_get(patch_chunk_p, &byte); 577 | 578 | if (res != 0) { 579 | return (res); 580 | } 581 | 582 | if (is_overflow(self_p->offset)) { 583 | return (-DETOOLS_CORRUPT_PATCH_OVERFLOW); 584 | } 585 | 586 | self_p->value |= ((byte & 0x7f) << self_p->offset); 587 | self_p->offset += 7; 588 | } while ((byte & 0x80) != 0); 589 | 590 | *size_p = self_p->value; 591 | 592 | return (0); 593 | } 594 | 595 | static int patch_reader_crle_decompress_idle( 596 | struct detools_apply_patch_patch_reader_t *self_p, 597 | struct detools_apply_patch_patch_reader_crle_t *crle_p) 598 | { 599 | int res; 600 | uint8_t kind; 601 | 602 | res = chunk_get(self_p->patch_chunk_p, &kind); 603 | 604 | if (res != 0) { 605 | return (res); 606 | } 607 | 608 | res = 2; 609 | 610 | switch (kind) { 611 | 612 | case 0: 613 | crle_p->state = detools_crle_state_scattered_size_t; 614 | unpack_usize_init(&crle_p->kind.scattered.size); 615 | break; 616 | 617 | case 1: 618 | crle_p->state = detools_crle_state_repeated_repetitions_t; 619 | unpack_usize_init(&crle_p->kind.repeated.size); 620 | break; 621 | 622 | default: 623 | res = -DETOOLS_CORRUPT_PATCH_CRLE_KIND; 624 | break; 625 | } 626 | 627 | return (res); 628 | } 629 | 630 | static int patch_reader_crle_decompress_scattered_size( 631 | struct detools_apply_patch_patch_reader_t *self_p, 632 | struct detools_apply_patch_patch_reader_crle_t *crle_p) 633 | { 634 | int res; 635 | int size; 636 | 637 | res = unpack_usize(&crle_p->kind.scattered.size, 638 | self_p->patch_chunk_p, 639 | &size); 640 | 641 | if (res != 0) { 642 | return (res); 643 | } 644 | 645 | crle_p->state = detools_crle_state_scattered_data_t; 646 | crle_p->kind.scattered.number_of_bytes_left = (size_t)size; 647 | 648 | return (2); 649 | } 650 | 651 | static int patch_reader_crle_decompress_scattered_data( 652 | struct detools_apply_patch_patch_reader_t *self_p, 653 | struct detools_apply_patch_patch_reader_crle_t *crle_p, 654 | uint8_t *buf_p, 655 | size_t *size_p) 656 | { 657 | int res; 658 | 659 | *size_p = MIN(*size_p, crle_p->kind.scattered.number_of_bytes_left); 660 | res = chunk_read(self_p->patch_chunk_p, buf_p, size_p); 661 | 662 | if (res != 0) { 663 | return (res); 664 | } 665 | 666 | crle_p->kind.scattered.number_of_bytes_left -= *size_p; 667 | 668 | if (crle_p->kind.scattered.number_of_bytes_left == 0) { 669 | crle_p->state = detools_crle_state_idle_t; 670 | } 671 | 672 | return (0); 673 | } 674 | 675 | static int patch_reader_crle_decompress_repeated_repetitions( 676 | struct detools_apply_patch_patch_reader_t *self_p, 677 | struct detools_apply_patch_patch_reader_crle_t *crle_p) 678 | { 679 | int res; 680 | int repetitions; 681 | 682 | res = unpack_usize(&crle_p->kind.repeated.size, 683 | self_p->patch_chunk_p, 684 | &repetitions); 685 | 686 | if (res != 0) { 687 | return (res); 688 | } 689 | 690 | crle_p->state = detools_crle_state_repeated_data_t; 691 | crle_p->kind.repeated.number_of_bytes_left = (size_t)repetitions; 692 | 693 | return (2); 694 | } 695 | 696 | static int patch_reader_crle_decompress_repeated_data( 697 | struct detools_apply_patch_patch_reader_t *self_p, 698 | struct detools_apply_patch_patch_reader_crle_t *crle_p) 699 | { 700 | int res; 701 | 702 | res = chunk_get(self_p->patch_chunk_p, 703 | &crle_p->kind.repeated.value); 704 | 705 | if (res != 0) { 706 | return (res); 707 | } 708 | 709 | crle_p->state = detools_crle_state_repeated_data_read_t; 710 | 711 | return (2); 712 | } 713 | 714 | static int patch_reader_crle_decompress_repeated_data_read( 715 | struct detools_apply_patch_patch_reader_crle_t *crle_p, 716 | uint8_t *buf_p, 717 | size_t *size_p) 718 | { 719 | size_t size; 720 | size_t i; 721 | 722 | size = MIN(*size_p, crle_p->kind.repeated.number_of_bytes_left); 723 | 724 | for (i = 0; i < size; i++) { 725 | buf_p[i] = crle_p->kind.repeated.value; 726 | } 727 | 728 | *size_p = size; 729 | crle_p->kind.repeated.number_of_bytes_left -= size; 730 | 731 | if (crle_p->kind.repeated.number_of_bytes_left == 0) { 732 | crle_p->state = detools_crle_state_idle_t; 733 | } 734 | 735 | return (0); 736 | } 737 | 738 | static int patch_reader_crle_decompress( 739 | struct detools_apply_patch_patch_reader_t *self_p, 740 | uint8_t *buf_p, 741 | size_t *size_p) 742 | { 743 | int res; 744 | struct detools_apply_patch_patch_reader_crle_t *crle_p; 745 | 746 | crle_p = &self_p->compression.crle; 747 | 748 | do { 749 | switch (crle_p->state) { 750 | 751 | case detools_crle_state_idle_t: 752 | res = patch_reader_crle_decompress_idle(self_p, crle_p); 753 | break; 754 | 755 | case detools_crle_state_scattered_size_t: 756 | res = patch_reader_crle_decompress_scattered_size(self_p, crle_p); 757 | break; 758 | 759 | case detools_crle_state_scattered_data_t: 760 | res = patch_reader_crle_decompress_scattered_data(self_p, 761 | crle_p, 762 | buf_p, 763 | size_p); 764 | break; 765 | 766 | case detools_crle_state_repeated_repetitions_t: 767 | res = patch_reader_crle_decompress_repeated_repetitions(self_p, 768 | crle_p); 769 | break; 770 | 771 | case detools_crle_state_repeated_data_t: 772 | res = patch_reader_crle_decompress_repeated_data(self_p, crle_p); 773 | break; 774 | 775 | case detools_crle_state_repeated_data_read_t: 776 | res = patch_reader_crle_decompress_repeated_data_read(crle_p, 777 | buf_p, 778 | size_p); 779 | break; 780 | 781 | default: 782 | res = -DETOOLS_INTERNAL_ERROR; 783 | break; 784 | } 785 | } while (res == 2); 786 | 787 | return (res); 788 | } 789 | 790 | static int patch_reader_crle_destroy( 791 | struct detools_apply_patch_patch_reader_t *self_p) 792 | { 793 | (void)self_p; 794 | 795 | return (0); 796 | } 797 | 798 | static int patch_reader_crle_init(struct detools_apply_patch_patch_reader_t *self_p) 799 | { 800 | 801 | struct detools_apply_patch_patch_reader_crle_t *crle_p; 802 | 803 | crle_p = &self_p->compression.crle; 804 | crle_p->state = detools_crle_state_idle_t; 805 | self_p->destroy = patch_reader_crle_destroy; 806 | self_p->decompress = patch_reader_crle_decompress; 807 | 808 | return (0); 809 | } 810 | 811 | #endif 812 | 813 | /* 814 | * Patch reader. 815 | */ 816 | 817 | /** 818 | * Initialize given patch reader. 819 | */ 820 | static int patch_reader_init(struct detools_apply_patch_patch_reader_t *self_p, 821 | struct detools_apply_patch_chunk_t *patch_chunk_p, 822 | size_t patch_size, 823 | int compression) 824 | { 825 | int res; 826 | 827 | #if DETOOLS_CONFIG_COMPRESSION_NONE != 1 828 | (void)patch_size; 829 | #endif 830 | 831 | self_p->patch_chunk_p = patch_chunk_p; 832 | self_p->size.state = detools_unpack_usize_state_first_t; 833 | 834 | switch (compression) { 835 | 836 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 837 | case COMPRESSION_NONE: 838 | res = patch_reader_none_init(self_p, patch_size); 839 | break; 840 | #endif 841 | 842 | #if DETOOLS_CONFIG_COMPRESSION_LZMA == 1 843 | case COMPRESSION_LZMA: 844 | res = patch_reader_lzma_init(self_p); 845 | break; 846 | #endif 847 | 848 | #if DETOOLS_CONFIG_COMPRESSION_CRLE == 1 849 | case COMPRESSION_CRLE: 850 | res = patch_reader_crle_init(self_p); 851 | break; 852 | #endif 853 | 854 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 855 | case COMPRESSION_HEATSHRINK: 856 | res = patch_reader_heatshrink_init(self_p); 857 | break; 858 | #endif 859 | 860 | default: 861 | res = -DETOOLS_BAD_COMPRESSION; 862 | break; 863 | } 864 | 865 | return (res); 866 | } 867 | 868 | static int patch_reader_dump(struct detools_apply_patch_patch_reader_t *self_p, 869 | int compression, 870 | detools_state_write_t state_write) 871 | { 872 | (void)self_p; 873 | (void)state_write; 874 | 875 | int res; 876 | 877 | res = 0; 878 | 879 | switch (compression) { 880 | 881 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 882 | case COMPRESSION_NONE: 883 | break; 884 | #endif 885 | 886 | #if DETOOLS_CONFIG_COMPRESSION_CRLE == 1 887 | case COMPRESSION_CRLE: 888 | break; 889 | #endif 890 | 891 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 892 | case COMPRESSION_HEATSHRINK: 893 | break; 894 | #endif 895 | 896 | default: 897 | res = -DETOOLS_NOT_IMPLEMENTED; 898 | break; 899 | } 900 | 901 | return (res); 902 | } 903 | 904 | static int patch_reader_restore(struct detools_apply_patch_patch_reader_t *self_p, 905 | struct detools_apply_patch_patch_reader_t *dumped_p, 906 | struct detools_apply_patch_chunk_t *patch_chunk_p, 907 | int compression, 908 | detools_state_read_t state_read) 909 | { 910 | (void)state_read; 911 | 912 | int res; 913 | 914 | res = 0; 915 | *self_p = *dumped_p; 916 | self_p->patch_chunk_p = patch_chunk_p; 917 | 918 | switch (compression) { 919 | 920 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 921 | case COMPRESSION_NONE: 922 | self_p->destroy = patch_reader_none_destroy; 923 | self_p->decompress = patch_reader_none_decompress; 924 | break; 925 | #endif 926 | 927 | #if DETOOLS_CONFIG_COMPRESSION_CRLE == 1 928 | case COMPRESSION_CRLE: 929 | self_p->destroy = patch_reader_crle_destroy; 930 | self_p->decompress = patch_reader_crle_decompress; 931 | break; 932 | #endif 933 | 934 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 935 | case COMPRESSION_HEATSHRINK: 936 | self_p->destroy = patch_reader_heatshrink_destroy; 937 | self_p->decompress = patch_reader_heatshrink_decompress; 938 | break; 939 | #endif 940 | 941 | default: 942 | res = -DETOOLS_NOT_IMPLEMENTED; 943 | break; 944 | } 945 | 946 | return (res); 947 | } 948 | 949 | /** 950 | * Try to decompress given number of bytes. 951 | * 952 | * @return zero(0) if at least one byte was decompressed, one(1) if 953 | * zero bytes were decompressed and more input is needed, or 954 | * negative error code. 955 | */ 956 | static int patch_reader_decompress( 957 | struct detools_apply_patch_patch_reader_t *self_p, 958 | uint8_t *buf_p, 959 | size_t *size_p) 960 | { 961 | return (self_p->decompress(self_p, buf_p, size_p)); 962 | } 963 | 964 | /** 965 | * Unpack a size value. 966 | */ 967 | static int patch_reader_unpack_size( 968 | struct detools_apply_patch_patch_reader_t *self_p, 969 | int *size_p) 970 | { 971 | int res; 972 | uint8_t byte; 973 | size_t size; 974 | 975 | size = 1; 976 | 977 | do { 978 | switch (self_p->size.state) { 979 | 980 | case detools_unpack_usize_state_first_t: 981 | res = patch_reader_decompress(self_p, &byte, &size); 982 | 983 | if (res != 0) { 984 | return (res); 985 | } 986 | 987 | self_p->size.is_signed = ((byte & 0x40) == 0x40); 988 | self_p->size.value = (byte & 0x3f); 989 | self_p->size.offset = 6; 990 | self_p->size.state = detools_unpack_usize_state_consecutive_t; 991 | break; 992 | 993 | case detools_unpack_usize_state_consecutive_t: 994 | res = patch_reader_decompress(self_p, &byte, &size); 995 | 996 | if (res != 0) { 997 | return (res); 998 | } 999 | 1000 | if (is_overflow(self_p->size.offset)) { 1001 | return (-DETOOLS_CORRUPT_PATCH_OVERFLOW); 1002 | } 1003 | 1004 | self_p->size.value |= ((byte & 0x7f) << self_p->size.offset); 1005 | self_p->size.offset += 7; 1006 | break; 1007 | 1008 | default: 1009 | return (-DETOOLS_INTERNAL_ERROR); 1010 | } 1011 | } while ((byte & 0x80) != 0); 1012 | 1013 | /* Done, fix sign. */ 1014 | self_p->size.state = detools_unpack_usize_state_first_t; 1015 | 1016 | if (self_p->size.is_signed) { 1017 | self_p->size.value *= -1; 1018 | } 1019 | 1020 | *size_p = self_p->size.value; 1021 | 1022 | return (res); 1023 | } 1024 | 1025 | static int common_process_size( 1026 | struct detools_apply_patch_patch_reader_t *patch_reader_p, 1027 | size_t to_pos, 1028 | size_t to_size, 1029 | int *size_p) 1030 | { 1031 | int res; 1032 | 1033 | res = patch_reader_unpack_size(patch_reader_p, size_p); 1034 | 1035 | if (res != 0) { 1036 | return (res); 1037 | } 1038 | 1039 | if (to_pos + (size_t)*size_p > to_size) { 1040 | return (-DETOOLS_CORRUPT_PATCH); 1041 | } 1042 | 1043 | return (res); 1044 | } 1045 | 1046 | /* 1047 | * Low level sequential patch type functionality. 1048 | */ 1049 | 1050 | static int process_init(struct detools_apply_patch_t *self_p) 1051 | { 1052 | int patch_type; 1053 | uint8_t byte; 1054 | int res; 1055 | int to_size; 1056 | 1057 | if (chunk_get(&self_p->chunk, &byte) != 0) { 1058 | return (-DETOOLS_SHORT_HEADER); 1059 | } 1060 | 1061 | patch_type = ((byte >> 4) & 0x7); 1062 | self_p->compression = (byte & 0xf); 1063 | 1064 | if (patch_type != PATCH_TYPE_SEQUENTIAL) { 1065 | return (-DETOOLS_BAD_PATCH_TYPE); 1066 | } 1067 | 1068 | res = chunk_unpack_header_size(&self_p->chunk, &to_size); 1069 | 1070 | if (res != 0) { 1071 | return (res); 1072 | } 1073 | 1074 | res = patch_reader_init(&self_p->patch_reader, 1075 | &self_p->chunk, 1076 | self_p->patch_size - self_p->chunk.offset, 1077 | self_p->compression); 1078 | 1079 | if (res != 0) { 1080 | return (res); 1081 | } 1082 | 1083 | if (to_size < 0) { 1084 | return (-DETOOLS_CORRUPT_PATCH); 1085 | } 1086 | 1087 | self_p->to_offset = 0; 1088 | self_p->to_size = (size_t)to_size; 1089 | 1090 | if (to_size > 0) { 1091 | self_p->state = detools_apply_patch_state_dfpatch_size_t; 1092 | } else { 1093 | self_p->state = detools_apply_patch_state_done_t; 1094 | } 1095 | 1096 | return (res); 1097 | } 1098 | 1099 | static int process_dfpatch_size(struct detools_apply_patch_t *self_p) 1100 | { 1101 | int res; 1102 | int size; 1103 | 1104 | res = patch_reader_unpack_size(&self_p->patch_reader, &size); 1105 | 1106 | if (res != 0) { 1107 | return (res); 1108 | } 1109 | 1110 | if (size > 0) { 1111 | return (-DETOOLS_NOT_IMPLEMENTED); 1112 | } 1113 | 1114 | self_p->state = detools_apply_patch_state_diff_size_t; 1115 | 1116 | return (0); 1117 | } 1118 | 1119 | static int process_size(struct detools_apply_patch_t *self_p, 1120 | enum detools_apply_patch_state_t next_state) 1121 | { 1122 | int res; 1123 | int size; 1124 | 1125 | res = common_process_size(&self_p->patch_reader, 1126 | self_p->to_offset, 1127 | self_p->to_size, 1128 | &size); 1129 | 1130 | if (res != 0) { 1131 | return (res); 1132 | } 1133 | 1134 | self_p->state = next_state; 1135 | self_p->chunk_size = (size_t)size; 1136 | 1137 | return (res); 1138 | } 1139 | 1140 | static int process_data(struct detools_apply_patch_t *self_p, 1141 | enum detools_apply_patch_state_t next_state) 1142 | { 1143 | int res; 1144 | size_t i; 1145 | uint8_t to[128]; 1146 | size_t to_size; 1147 | uint8_t from[128]; 1148 | 1149 | to_size = MIN(sizeof(to), self_p->chunk_size); 1150 | 1151 | if (to_size == 0) { 1152 | self_p->state = next_state; 1153 | 1154 | return (0); 1155 | } 1156 | 1157 | res = patch_reader_decompress(&self_p->patch_reader, 1158 | &to[0], 1159 | &to_size); 1160 | 1161 | if (res != 0) { 1162 | return (res); 1163 | } 1164 | 1165 | if (next_state == detools_apply_patch_state_extra_size_t) { 1166 | res = self_p->from_read(self_p->arg_p, &from[0], to_size); 1167 | 1168 | if (res != 0) { 1169 | return (-DETOOLS_IO_FAILED); 1170 | } 1171 | 1172 | self_p->from_offset += to_size; 1173 | 1174 | for (i = 0; i < to_size; i++) { 1175 | to[i] = (uint8_t)(to[i] + from[i]); 1176 | } 1177 | } 1178 | 1179 | self_p->to_offset += to_size; 1180 | self_p->chunk_size -= to_size; 1181 | 1182 | res = self_p->to_write(self_p->arg_p, &to[0], to_size); 1183 | 1184 | if (res != 0) { 1185 | return (-DETOOLS_IO_FAILED); 1186 | } 1187 | 1188 | return (res); 1189 | } 1190 | 1191 | static int process_diff_size(struct detools_apply_patch_t *self_p) 1192 | { 1193 | return (process_size(self_p, detools_apply_patch_state_diff_data_t)); 1194 | } 1195 | 1196 | static int process_diff_data(struct detools_apply_patch_t *self_p) 1197 | { 1198 | return (process_data(self_p, detools_apply_patch_state_extra_size_t)); 1199 | } 1200 | 1201 | static int process_extra_size(struct detools_apply_patch_t *self_p) 1202 | { 1203 | return (process_size(self_p, detools_apply_patch_state_extra_data_t)); 1204 | } 1205 | 1206 | static int process_extra_data(struct detools_apply_patch_t *self_p) 1207 | { 1208 | return (process_data(self_p, detools_apply_patch_state_adjustment_t)); 1209 | } 1210 | 1211 | static int process_adjustment(struct detools_apply_patch_t *self_p) 1212 | { 1213 | int res; 1214 | int offset; 1215 | 1216 | res = patch_reader_unpack_size(&self_p->patch_reader, &offset); 1217 | 1218 | if (res != 0) { 1219 | return (res); 1220 | } 1221 | 1222 | res = self_p->from_seek(self_p->arg_p, offset); 1223 | 1224 | if (res != 0) { 1225 | return (-DETOOLS_IO_FAILED); 1226 | } 1227 | 1228 | self_p->from_offset += offset; 1229 | 1230 | if (self_p->to_offset == self_p->to_size) { 1231 | self_p->state = detools_apply_patch_state_done_t; 1232 | } else { 1233 | self_p->state = detools_apply_patch_state_diff_size_t; 1234 | } 1235 | 1236 | return (res); 1237 | } 1238 | 1239 | static int apply_patch_process_once(struct detools_apply_patch_t *self_p) 1240 | { 1241 | int res; 1242 | 1243 | switch (self_p->state) { 1244 | 1245 | case detools_apply_patch_state_init_t: 1246 | res = process_init(self_p); 1247 | break; 1248 | 1249 | case detools_apply_patch_state_dfpatch_size_t: 1250 | res = process_dfpatch_size(self_p); 1251 | break; 1252 | 1253 | case detools_apply_patch_state_diff_size_t: 1254 | res = process_diff_size(self_p); 1255 | break; 1256 | 1257 | case detools_apply_patch_state_diff_data_t: 1258 | res = process_diff_data(self_p); 1259 | break; 1260 | 1261 | case detools_apply_patch_state_extra_size_t: 1262 | res = process_extra_size(self_p); 1263 | break; 1264 | 1265 | case detools_apply_patch_state_extra_data_t: 1266 | res = process_extra_data(self_p); 1267 | break; 1268 | 1269 | case detools_apply_patch_state_adjustment_t: 1270 | res = process_adjustment(self_p); 1271 | break; 1272 | 1273 | case detools_apply_patch_state_done_t: 1274 | res = -DETOOLS_ALREADY_DONE; 1275 | break; 1276 | 1277 | case detools_apply_patch_state_failed_t: 1278 | res = -DETOOLS_ALREADY_FAILED; 1279 | break; 1280 | 1281 | default: 1282 | res = -DETOOLS_INTERNAL_ERROR; 1283 | break; 1284 | } 1285 | 1286 | if (res < 0) { 1287 | self_p->state = detools_apply_patch_state_failed_t; 1288 | } 1289 | 1290 | return (res); 1291 | } 1292 | 1293 | static int apply_patch_common_finalize( 1294 | int res, 1295 | struct detools_apply_patch_patch_reader_t *patch_reader_p, 1296 | size_t to_size) 1297 | { 1298 | if (res == 1) { 1299 | res = -DETOOLS_NOT_ENOUGH_PATCH_DATA; 1300 | } 1301 | 1302 | if (res == -DETOOLS_ALREADY_DONE) { 1303 | res = 0; 1304 | } 1305 | 1306 | if (patch_reader_p->destroy != NULL) { 1307 | if (res == 0) { 1308 | res = patch_reader_p->destroy(patch_reader_p); 1309 | } else { 1310 | (void)patch_reader_p->destroy(patch_reader_p); 1311 | } 1312 | } 1313 | 1314 | if (res == 0) { 1315 | res = (int)to_size; 1316 | } 1317 | 1318 | return (res); 1319 | } 1320 | 1321 | int detools_apply_patch_init(struct detools_apply_patch_t *self_p, 1322 | detools_read_t from_read, 1323 | detools_seek_t from_seek, 1324 | size_t patch_size, 1325 | detools_write_t to_write, 1326 | void *arg_p) 1327 | { 1328 | self_p->from_read = from_read; 1329 | self_p->from_seek = from_seek; 1330 | self_p->patch_size = patch_size; 1331 | self_p->patch_offset = 0; 1332 | self_p->to_write = to_write; 1333 | self_p->from_offset = 0; 1334 | self_p->arg_p = arg_p; 1335 | self_p->state = detools_apply_patch_state_init_t; 1336 | self_p->patch_reader.destroy = NULL; 1337 | 1338 | return (0); 1339 | } 1340 | 1341 | int detools_apply_patch_dump(struct detools_apply_patch_t *self_p, 1342 | detools_state_write_t state_write) 1343 | { 1344 | int res; 1345 | 1346 | res = state_write(self_p->arg_p, self_p, sizeof(*self_p)); 1347 | 1348 | if (res != 0) { 1349 | return (-DETOOLS_IO_FAILED); 1350 | } 1351 | 1352 | if (self_p->state == detools_apply_patch_state_init_t) { 1353 | return (0); 1354 | } 1355 | 1356 | return (patch_reader_dump(&self_p->patch_reader, 1357 | self_p->compression, 1358 | state_write)); 1359 | } 1360 | 1361 | int detools_apply_patch_restore(struct detools_apply_patch_t *self_p, 1362 | detools_state_read_t state_read) 1363 | { 1364 | int res; 1365 | struct detools_apply_patch_t dumped; 1366 | 1367 | res = state_read(self_p->arg_p, &dumped, sizeof(dumped)); 1368 | 1369 | if (res != 0) { 1370 | return (-DETOOLS_IO_FAILED); 1371 | } 1372 | 1373 | self_p->state = dumped.state; 1374 | self_p->patch_size = dumped.patch_size; 1375 | 1376 | if (self_p->state == detools_apply_patch_state_init_t) { 1377 | return (0); 1378 | } 1379 | 1380 | self_p->compression = dumped.compression; 1381 | self_p->patch_offset = dumped.patch_offset; 1382 | self_p->to_offset = dumped.to_offset; 1383 | self_p->to_size = dumped.to_size; 1384 | self_p->from_offset = dumped.from_offset; 1385 | self_p->chunk_size = dumped.chunk_size; 1386 | 1387 | res = self_p->from_seek(self_p->arg_p, self_p->from_offset); 1388 | 1389 | if (res != 0) { 1390 | return (-DETOOLS_IO_FAILED); 1391 | } 1392 | 1393 | return (patch_reader_restore(&self_p->patch_reader, 1394 | &dumped.patch_reader, 1395 | &self_p->chunk, 1396 | self_p->compression, 1397 | state_read)); 1398 | } 1399 | 1400 | size_t detools_apply_patch_get_patch_offset(struct detools_apply_patch_t *self_p) 1401 | { 1402 | return (self_p->patch_offset); 1403 | } 1404 | 1405 | size_t detools_apply_patch_get_to_offset(struct detools_apply_patch_t *self_p) 1406 | { 1407 | return (self_p->to_offset); 1408 | } 1409 | 1410 | int detools_apply_patch_process(struct detools_apply_patch_t *self_p, 1411 | const uint8_t *patch_p, 1412 | size_t size) 1413 | { 1414 | int res; 1415 | 1416 | res = 0; 1417 | self_p->patch_offset += size; 1418 | self_p->chunk.buf_p = patch_p; 1419 | self_p->chunk.size = size; 1420 | self_p->chunk.offset = 0; 1421 | 1422 | while (chunk_available(&self_p->chunk) && (res >= 0)) { 1423 | res = apply_patch_process_once(self_p); 1424 | } 1425 | 1426 | if (res == 1) { 1427 | res = 0; 1428 | } 1429 | 1430 | return (res); 1431 | } 1432 | 1433 | int detools_apply_patch_finalize(struct detools_apply_patch_t *self_p) 1434 | { 1435 | int res; 1436 | 1437 | self_p->chunk.size = 0; 1438 | self_p->chunk.offset = 0; 1439 | 1440 | do { 1441 | res = apply_patch_process_once(self_p); 1442 | } while (res == 0); 1443 | 1444 | return (apply_patch_common_finalize(res, 1445 | &self_p->patch_reader, 1446 | self_p->to_size)); 1447 | } 1448 | 1449 | /* 1450 | * Low level in-place patch type functionality. 1451 | */ 1452 | 1453 | static int in_place_all_steps_completed(struct detools_apply_patch_in_place_t *self_p) 1454 | { 1455 | int res; 1456 | 1457 | res = 0; 1458 | 1459 | if (self_p->step_set != NULL) { 1460 | res = self_p->step_set(self_p->arg_p, 0); 1461 | 1462 | if (res != 0) { 1463 | res = -DETOOLS_STEP_SET_FAILED; 1464 | } 1465 | } 1466 | 1467 | return (res); 1468 | } 1469 | 1470 | static int in_place_is_step_completed(struct detools_apply_patch_in_place_t *self_p, 1471 | bool *res_p) 1472 | { 1473 | int res; 1474 | int completed_step; 1475 | 1476 | if (self_p->step_get != NULL) { 1477 | res = self_p->step_get(self_p->arg_p, &completed_step); 1478 | 1479 | if (res != 0) { 1480 | return (-DETOOLS_STEP_GET_FAILED); 1481 | } 1482 | 1483 | *res_p = (self_p->ongoing_step <= completed_step); 1484 | } else { 1485 | *res_p = false; 1486 | } 1487 | 1488 | return (0); 1489 | } 1490 | 1491 | static int in_place_next_step(struct detools_apply_patch_in_place_t *self_p) 1492 | { 1493 | int res; 1494 | bool is_step_completed; 1495 | 1496 | res = 0; 1497 | 1498 | if (self_p->step_set != NULL) { 1499 | res = in_place_is_step_completed(self_p, &is_step_completed); 1500 | 1501 | if (res != 0) { 1502 | return (res); 1503 | } 1504 | 1505 | if (!is_step_completed) { 1506 | res = self_p->step_set(self_p->arg_p, self_p->ongoing_step); 1507 | 1508 | if (res != 0) { 1509 | res = -DETOOLS_STEP_SET_FAILED; 1510 | } 1511 | } 1512 | } 1513 | 1514 | self_p->ongoing_step++; 1515 | 1516 | return (res); 1517 | } 1518 | 1519 | static int in_place_mem_read(struct detools_apply_patch_in_place_t *self_p, 1520 | void *dst_p, 1521 | uintptr_t src, 1522 | size_t size) 1523 | { 1524 | int res; 1525 | bool is_step_completed; 1526 | 1527 | res = in_place_is_step_completed(self_p, &is_step_completed); 1528 | 1529 | if (res != 0) { 1530 | return (res); 1531 | } 1532 | 1533 | if (!is_step_completed) { 1534 | return (self_p->mem_read(self_p->arg_p, dst_p, src, size)); 1535 | } else { 1536 | memset(dst_p, 0, size); 1537 | 1538 | return (0); 1539 | } 1540 | } 1541 | 1542 | static int in_place_mem_write(struct detools_apply_patch_in_place_t *self_p, 1543 | uintptr_t dst, 1544 | void *src_p, 1545 | size_t size) 1546 | { 1547 | int res; 1548 | bool is_step_completed; 1549 | 1550 | res = in_place_is_step_completed(self_p, &is_step_completed); 1551 | 1552 | if (res != 0) { 1553 | return (res); 1554 | } 1555 | 1556 | if (!is_step_completed) { 1557 | return (self_p->mem_write(self_p->arg_p, dst, src_p, size)); 1558 | } else { 1559 | return (0); 1560 | } 1561 | } 1562 | 1563 | static int in_place_mem_erase(struct detools_apply_patch_in_place_t *self_p, 1564 | uintptr_t addr, 1565 | size_t size) 1566 | { 1567 | int res; 1568 | bool is_step_completed; 1569 | 1570 | res = in_place_is_step_completed(self_p, &is_step_completed); 1571 | 1572 | if (res != 0) { 1573 | return (res); 1574 | } 1575 | 1576 | if (!is_step_completed) { 1577 | return (self_p->mem_erase(self_p->arg_p, addr, size)); 1578 | } else { 1579 | return (0); 1580 | } 1581 | } 1582 | 1583 | static int in_place_shift_memory(struct detools_apply_patch_in_place_t *self_p, 1584 | size_t memory_size, 1585 | size_t from_size) 1586 | { 1587 | size_t i; 1588 | size_t number_of_segments; 1589 | int res; 1590 | size_t read_address; 1591 | size_t write_address; 1592 | uint8_t buf[128]; 1593 | size_t offset; 1594 | size_t size; 1595 | 1596 | number_of_segments = DIV_CEIL(MIN(from_size, memory_size - self_p->shift_size), 1597 | self_p->segment_size); 1598 | read_address = ((number_of_segments - 1) * self_p->segment_size); 1599 | write_address = (read_address + self_p->shift_size); 1600 | 1601 | for (i = 0; i < number_of_segments; i++) { 1602 | /* Erase segment to write to. */ 1603 | res = in_place_mem_erase(self_p, 1604 | write_address, 1605 | self_p->segment_size); 1606 | 1607 | if (res != 0) { 1608 | return (res); 1609 | } 1610 | 1611 | /* Copy data to erased segment. */ 1612 | offset = 0; 1613 | 1614 | while (offset < self_p->segment_size) { 1615 | size = MIN(sizeof(buf), self_p->segment_size - offset); 1616 | res = in_place_mem_read(self_p, 1617 | &buf[0], 1618 | read_address + offset, 1619 | size); 1620 | 1621 | if (res != 0) { 1622 | return (res); 1623 | } 1624 | 1625 | res = in_place_mem_write(self_p, 1626 | write_address + offset, 1627 | &buf[0], 1628 | size); 1629 | 1630 | if (res != 0) { 1631 | return (res); 1632 | } 1633 | 1634 | offset += size; 1635 | } 1636 | 1637 | res = in_place_next_step(self_p); 1638 | 1639 | if (res != 0) { 1640 | return (res); 1641 | } 1642 | 1643 | write_address -= self_p->segment_size; 1644 | read_address -= self_p->segment_size; 1645 | } 1646 | 1647 | return (0); 1648 | } 1649 | 1650 | static int in_place_read_header(struct detools_apply_patch_in_place_t *self_p, 1651 | int *compression_p, 1652 | int *memory_size_p, 1653 | int *segment_size_p, 1654 | int *shift_size_p, 1655 | int *from_size_p, 1656 | int *to_size_p) 1657 | { 1658 | int patch_type; 1659 | uint8_t byte; 1660 | int res; 1661 | 1662 | if (chunk_get(&self_p->chunk, &byte) != 0) { 1663 | return (-DETOOLS_SHORT_HEADER); 1664 | } 1665 | 1666 | patch_type = ((byte >> 4) & 0x7); 1667 | *compression_p = (byte & 0xf); 1668 | 1669 | if (patch_type != PATCH_TYPE_IN_PLACE) { 1670 | return (-DETOOLS_BAD_PATCH_TYPE); 1671 | } 1672 | 1673 | res = chunk_unpack_header_size(&self_p->chunk, memory_size_p); 1674 | 1675 | if (res != 0) { 1676 | return (res); 1677 | } 1678 | 1679 | res = chunk_unpack_header_size(&self_p->chunk, segment_size_p); 1680 | 1681 | if (res != 0) { 1682 | return (res); 1683 | } 1684 | 1685 | res = chunk_unpack_header_size(&self_p->chunk, shift_size_p); 1686 | 1687 | if (res != 0) { 1688 | return (res); 1689 | } 1690 | 1691 | res = chunk_unpack_header_size(&self_p->chunk, from_size_p); 1692 | 1693 | if (res != 0) { 1694 | return (res); 1695 | } 1696 | 1697 | return (chunk_unpack_header_size(&self_p->chunk, to_size_p)); 1698 | } 1699 | 1700 | static int in_place_process_init(struct detools_apply_patch_in_place_t *self_p) 1701 | { 1702 | int res; 1703 | int compression; 1704 | int memory_size; 1705 | int segment_size; 1706 | int shift_size; 1707 | int from_size; 1708 | int to_size; 1709 | 1710 | res = in_place_read_header(self_p, 1711 | &compression, 1712 | &memory_size, 1713 | &segment_size, 1714 | &shift_size, 1715 | &from_size, 1716 | &to_size); 1717 | 1718 | if (res != 0) { 1719 | return (res); 1720 | } 1721 | 1722 | res = patch_reader_init(&self_p->patch_reader, 1723 | &self_p->chunk, 1724 | self_p->patch_size - self_p->chunk.offset, 1725 | compression); 1726 | 1727 | if (res != 0) { 1728 | return (res); 1729 | } 1730 | 1731 | if (to_size < 0) { 1732 | return (-DETOOLS_CORRUPT_PATCH); 1733 | } 1734 | 1735 | self_p->to_pos = 0; 1736 | self_p->segment_size = (size_t)segment_size; 1737 | self_p->shift_size = (size_t)shift_size; 1738 | self_p->to_size = (size_t)to_size; 1739 | self_p->segment.index = 0; 1740 | 1741 | if (to_size > 0) { 1742 | res = in_place_shift_memory(self_p, 1743 | (size_t)memory_size, 1744 | (size_t)from_size); 1745 | 1746 | if (res != 0) { 1747 | return (res); 1748 | } 1749 | 1750 | self_p->state = detools_apply_patch_state_dfpatch_size_t; 1751 | } else { 1752 | self_p->state = detools_apply_patch_state_done_t; 1753 | } 1754 | 1755 | return (res); 1756 | } 1757 | 1758 | static int in_place_process_dfpatch_size( 1759 | struct detools_apply_patch_in_place_t *self_p) 1760 | { 1761 | int res; 1762 | int size; 1763 | 1764 | res = patch_reader_unpack_size(&self_p->patch_reader, &size); 1765 | 1766 | if (res != 0) { 1767 | return (res); 1768 | } 1769 | 1770 | if (size > 0) { 1771 | return (-DETOOLS_NOT_IMPLEMENTED); 1772 | } 1773 | 1774 | self_p->state = detools_apply_patch_state_diff_size_t; 1775 | self_p->segment.from_offset = 1776 | (int)MAX(self_p->segment_size * (self_p->segment.index + 1), 1777 | self_p->shift_size); 1778 | self_p->segment.to_offset = (self_p->segment.index * self_p->segment_size); 1779 | self_p->segment.to_size = MIN(self_p->segment_size, 1780 | self_p->to_size - self_p->segment.to_offset); 1781 | self_p->segment.to_pos = 0; 1782 | self_p->segment.index++; 1783 | 1784 | return (in_place_mem_erase(self_p, 1785 | self_p->segment.to_offset, 1786 | self_p->segment.to_size)); 1787 | } 1788 | 1789 | static int in_place_process_size(struct detools_apply_patch_in_place_t *self_p, 1790 | enum detools_apply_patch_state_t next_state) 1791 | { 1792 | int res; 1793 | int size; 1794 | 1795 | res = common_process_size(&self_p->patch_reader, 1796 | self_p->to_pos, 1797 | self_p->to_size, 1798 | &size); 1799 | 1800 | if (res != 0) { 1801 | return (res); 1802 | } 1803 | 1804 | self_p->state = next_state; 1805 | self_p->chunk_size = (size_t)size; 1806 | 1807 | return (0); 1808 | } 1809 | 1810 | static int in_place_process_data(struct detools_apply_patch_in_place_t *self_p, 1811 | enum detools_apply_patch_state_t next_state) 1812 | { 1813 | int res; 1814 | size_t i; 1815 | uint8_t to[128]; 1816 | size_t to_size; 1817 | uint8_t from[128]; 1818 | 1819 | to_size = MIN(sizeof(to), self_p->chunk_size); 1820 | 1821 | if (to_size == 0) { 1822 | self_p->state = next_state; 1823 | 1824 | return (0); 1825 | } 1826 | 1827 | res = patch_reader_decompress(&self_p->patch_reader, 1828 | &to[0], 1829 | &to_size); 1830 | 1831 | if (res != 0) { 1832 | return (res); 1833 | } 1834 | 1835 | if (next_state == detools_apply_patch_state_extra_size_t) { 1836 | res = in_place_mem_read(self_p, 1837 | &from[0], 1838 | (size_t)self_p->segment.from_offset, 1839 | to_size); 1840 | 1841 | if (res != 0) { 1842 | return (-DETOOLS_IO_FAILED); 1843 | } 1844 | 1845 | self_p->segment.from_offset += (int)to_size; 1846 | 1847 | for (i = 0; i < to_size; i++) { 1848 | to[i] = (uint8_t)(to[i] + from[i]); 1849 | } 1850 | } 1851 | 1852 | res = in_place_mem_write(self_p, 1853 | self_p->segment.to_pos + self_p->segment.to_offset, 1854 | &to[0], 1855 | to_size); 1856 | 1857 | if (res != 0) { 1858 | return (-DETOOLS_IO_FAILED); 1859 | } 1860 | 1861 | self_p->to_pos += to_size; 1862 | self_p->segment.to_pos += to_size; 1863 | self_p->chunk_size -= to_size; 1864 | 1865 | return (res); 1866 | } 1867 | 1868 | static int in_place_process_diff_size(struct detools_apply_patch_in_place_t *self_p) 1869 | { 1870 | return (in_place_process_size(self_p, detools_apply_patch_state_diff_data_t)); 1871 | } 1872 | 1873 | static int in_place_process_diff_data(struct detools_apply_patch_in_place_t *self_p) 1874 | { 1875 | return (in_place_process_data(self_p, detools_apply_patch_state_extra_size_t)); 1876 | } 1877 | 1878 | static int in_place_process_extra_size(struct detools_apply_patch_in_place_t *self_p) 1879 | { 1880 | return (in_place_process_size(self_p, detools_apply_patch_state_extra_data_t)); 1881 | } 1882 | 1883 | static int in_place_process_extra_data(struct detools_apply_patch_in_place_t *self_p) 1884 | { 1885 | return (in_place_process_data(self_p, detools_apply_patch_state_adjustment_t)); 1886 | } 1887 | 1888 | static int in_place_process_adjustment(struct detools_apply_patch_in_place_t *self_p) 1889 | { 1890 | int res; 1891 | int offset; 1892 | 1893 | res = patch_reader_unpack_size(&self_p->patch_reader, &offset); 1894 | 1895 | if (res != 0) { 1896 | return (res); 1897 | } 1898 | 1899 | if (self_p->to_pos == self_p->to_size) { 1900 | res = in_place_all_steps_completed(self_p); 1901 | self_p->state = detools_apply_patch_state_done_t; 1902 | } else if (self_p->segment.to_pos == self_p->segment.to_size) { 1903 | res = in_place_next_step(self_p); 1904 | self_p->state = detools_apply_patch_state_dfpatch_size_t; 1905 | } else { 1906 | self_p->segment.from_offset += offset; 1907 | self_p->state = detools_apply_patch_state_diff_size_t; 1908 | } 1909 | 1910 | return (res); 1911 | } 1912 | 1913 | static int apply_patch_in_place_process_once( 1914 | struct detools_apply_patch_in_place_t *self_p) 1915 | { 1916 | int res; 1917 | 1918 | switch (self_p->state) { 1919 | 1920 | case detools_apply_patch_state_init_t: 1921 | res = in_place_process_init(self_p); 1922 | break; 1923 | 1924 | case detools_apply_patch_state_dfpatch_size_t: 1925 | res = in_place_process_dfpatch_size(self_p); 1926 | break; 1927 | 1928 | case detools_apply_patch_state_diff_size_t: 1929 | res = in_place_process_diff_size(self_p); 1930 | break; 1931 | 1932 | case detools_apply_patch_state_diff_data_t: 1933 | res = in_place_process_diff_data(self_p); 1934 | break; 1935 | 1936 | case detools_apply_patch_state_extra_size_t: 1937 | res = in_place_process_extra_size(self_p); 1938 | break; 1939 | 1940 | case detools_apply_patch_state_extra_data_t: 1941 | res = in_place_process_extra_data(self_p); 1942 | break; 1943 | 1944 | case detools_apply_patch_state_adjustment_t: 1945 | res = in_place_process_adjustment(self_p); 1946 | break; 1947 | 1948 | case detools_apply_patch_state_done_t: 1949 | res = -DETOOLS_ALREADY_DONE; 1950 | break; 1951 | 1952 | case detools_apply_patch_state_failed_t: 1953 | res = -DETOOLS_ALREADY_FAILED; 1954 | break; 1955 | 1956 | default: 1957 | res = -DETOOLS_INTERNAL_ERROR; 1958 | break; 1959 | } 1960 | 1961 | if (res < 0) { 1962 | self_p->state = detools_apply_patch_state_failed_t; 1963 | } 1964 | 1965 | return (res); 1966 | } 1967 | 1968 | int detools_apply_patch_in_place_init( 1969 | struct detools_apply_patch_in_place_t *self_p, 1970 | detools_mem_read_t mem_read, 1971 | detools_mem_write_t mem_write, 1972 | detools_mem_erase_t mem_erase, 1973 | detools_step_set_t step_set, 1974 | detools_step_get_t step_get, 1975 | size_t patch_size, 1976 | void *arg_p) 1977 | { 1978 | self_p->mem_read = mem_read; 1979 | self_p->mem_write = mem_write; 1980 | self_p->mem_erase = mem_erase; 1981 | self_p->step_set = step_set; 1982 | self_p->step_get = step_get; 1983 | self_p->patch_size = patch_size; 1984 | self_p->arg_p = arg_p; 1985 | self_p->state = detools_apply_patch_state_init_t; 1986 | self_p->ongoing_step = 1; 1987 | self_p->patch_reader.destroy = NULL; 1988 | 1989 | return (0); 1990 | } 1991 | 1992 | int detools_apply_patch_in_place_process( 1993 | struct detools_apply_patch_in_place_t *self_p, 1994 | const uint8_t *patch_p, 1995 | size_t size) 1996 | { 1997 | int res; 1998 | 1999 | res = 0; 2000 | self_p->chunk.buf_p = patch_p; 2001 | self_p->chunk.size = size; 2002 | self_p->chunk.offset = 0; 2003 | 2004 | while (chunk_available(&self_p->chunk) && (res >= 0)) { 2005 | res = apply_patch_in_place_process_once(self_p); 2006 | } 2007 | 2008 | if (res == 1) { 2009 | res = 0; 2010 | } 2011 | 2012 | return (res); 2013 | } 2014 | 2015 | int detools_apply_patch_in_place_finalize( 2016 | struct detools_apply_patch_in_place_t *self_p) 2017 | { 2018 | int res; 2019 | 2020 | self_p->chunk.size = 0; 2021 | self_p->chunk.offset = 0; 2022 | 2023 | do { 2024 | res = apply_patch_in_place_process_once(self_p); 2025 | } while (res == 0); 2026 | 2027 | return (apply_patch_common_finalize(res, 2028 | &self_p->patch_reader, 2029 | self_p->to_size)); 2030 | } 2031 | 2032 | /* 2033 | * Callback functionality. 2034 | */ 2035 | 2036 | static int callbacks_process(struct detools_apply_patch_t *apply_patch_p, 2037 | detools_read_t patch_read, 2038 | size_t patch_size, 2039 | void *arg_p) 2040 | { 2041 | int res; 2042 | size_t patch_offset; 2043 | size_t chunk_size; 2044 | uint8_t chunk[512]; 2045 | 2046 | res = 0; 2047 | patch_offset = 0; 2048 | 2049 | while ((patch_offset < patch_size) && (res == 0)) { 2050 | chunk_size = MIN(patch_size - patch_offset, 512); 2051 | res = patch_read(arg_p, &chunk[0], chunk_size); 2052 | 2053 | if (res == 0) { 2054 | res = detools_apply_patch_process(apply_patch_p, 2055 | &chunk[0], 2056 | chunk_size); 2057 | patch_offset += chunk_size; 2058 | } else { 2059 | res = -DETOOLS_IO_FAILED; 2060 | } 2061 | } 2062 | 2063 | if (res == 0) { 2064 | res = detools_apply_patch_finalize(apply_patch_p); 2065 | } else { 2066 | (void)detools_apply_patch_finalize(apply_patch_p); 2067 | } 2068 | 2069 | return (res); 2070 | } 2071 | 2072 | int detools_apply_patch_callbacks(detools_read_t from_read, 2073 | detools_seek_t from_seek, 2074 | detools_read_t patch_read, 2075 | size_t patch_size, 2076 | detools_write_t to_write, 2077 | void *arg_p) 2078 | { 2079 | int res; 2080 | struct detools_apply_patch_t apply_patch; 2081 | 2082 | res = detools_apply_patch_init(&apply_patch, 2083 | from_read, 2084 | from_seek, 2085 | patch_size, 2086 | to_write, 2087 | arg_p); 2088 | 2089 | if (res != 0) { 2090 | return (res); 2091 | } 2092 | 2093 | return (callbacks_process(&apply_patch, patch_read, patch_size, arg_p)); 2094 | } 2095 | 2096 | static int in_place_callbacks_process( 2097 | struct detools_apply_patch_in_place_t *apply_patch_p, 2098 | detools_read_t patch_read, 2099 | size_t patch_size, 2100 | void *arg_p) 2101 | { 2102 | int res; 2103 | size_t patch_offset; 2104 | size_t chunk_size; 2105 | uint8_t chunk[512]; 2106 | 2107 | res = 0; 2108 | patch_offset = 0; 2109 | 2110 | while ((patch_offset < patch_size) && (res == 0)) { 2111 | chunk_size = MIN(patch_size - patch_offset, 512); 2112 | res = patch_read(arg_p, &chunk[0], chunk_size); 2113 | 2114 | if (res == 0) { 2115 | res = detools_apply_patch_in_place_process(apply_patch_p, 2116 | &chunk[0], 2117 | chunk_size); 2118 | patch_offset += chunk_size; 2119 | } else { 2120 | res = -DETOOLS_IO_FAILED; 2121 | } 2122 | } 2123 | 2124 | if (res == 0) { 2125 | res = detools_apply_patch_in_place_finalize(apply_patch_p); 2126 | } else { 2127 | (void)detools_apply_patch_in_place_finalize(apply_patch_p); 2128 | } 2129 | 2130 | return (res); 2131 | } 2132 | 2133 | int detools_apply_patch_in_place_callbacks(detools_mem_read_t mem_read, 2134 | detools_mem_write_t mem_write, 2135 | detools_mem_erase_t mem_erase, 2136 | detools_step_set_t step_set, 2137 | detools_step_get_t step_get, 2138 | detools_read_t patch_read, 2139 | size_t patch_size, 2140 | void *arg_p) 2141 | { 2142 | int res; 2143 | struct detools_apply_patch_in_place_t apply_patch; 2144 | 2145 | res = detools_apply_patch_in_place_init(&apply_patch, 2146 | mem_read, 2147 | mem_write, 2148 | mem_erase, 2149 | step_set, 2150 | step_get, 2151 | patch_size, 2152 | arg_p); 2153 | 2154 | if (res != 0) { 2155 | return (res); 2156 | } 2157 | 2158 | return (in_place_callbacks_process(&apply_patch, 2159 | patch_read, 2160 | patch_size, 2161 | arg_p)); 2162 | } 2163 | 2164 | /* 2165 | * File io functionality. 2166 | */ 2167 | 2168 | #if DETOOLS_CONFIG_FILE_IO == 1 2169 | 2170 | struct file_io_t { 2171 | FILE *ffrom_p; 2172 | FILE *fpatch_p; 2173 | FILE *fto_p; 2174 | }; 2175 | 2176 | static int file_size(FILE *file_p, size_t *size_p) 2177 | { 2178 | int res; 2179 | long size; 2180 | 2181 | res = fseek(file_p, 0, SEEK_END); 2182 | 2183 | if (res != 0) { 2184 | return (-DETOOLS_FILE_SEEK_FAILED); 2185 | } 2186 | 2187 | size = ftell(file_p); 2188 | 2189 | if (size <= 0) { 2190 | return (-DETOOLS_FILE_TELL_FAILED); 2191 | } 2192 | 2193 | *size_p = (size_t)size; 2194 | 2195 | res = fseek(file_p, 0, SEEK_SET); 2196 | 2197 | if (res != 0) { 2198 | return (-DETOOLS_FILE_SEEK_FAILED); 2199 | } 2200 | 2201 | return (res); 2202 | } 2203 | 2204 | static int file_io_init(struct file_io_t *self_p, 2205 | const char *from_p, 2206 | const char *patch_p, 2207 | const char *to_p, 2208 | size_t *patch_size_p) 2209 | { 2210 | int res; 2211 | FILE *file_p; 2212 | 2213 | res = -DETOOLS_FILE_OPEN_FAILED; 2214 | 2215 | /* From. */ 2216 | file_p = fopen(from_p, "rb"); 2217 | 2218 | if (file_p == NULL) { 2219 | return (res); 2220 | } 2221 | 2222 | self_p->ffrom_p = file_p; 2223 | 2224 | /* To. */ 2225 | file_p = fopen(to_p, "wb"); 2226 | 2227 | if (file_p == NULL) { 2228 | goto err1; 2229 | } 2230 | 2231 | self_p->fto_p = file_p; 2232 | 2233 | /* Patch. */ 2234 | file_p = fopen(patch_p, "rb"); 2235 | 2236 | if (file_p == NULL) { 2237 | goto err2; 2238 | } 2239 | 2240 | self_p->fpatch_p = file_p; 2241 | res = file_size(self_p->fpatch_p, patch_size_p); 2242 | 2243 | if (res != 0) { 2244 | goto err3; 2245 | } 2246 | 2247 | return (res); 2248 | 2249 | err3: 2250 | fclose(self_p->fpatch_p); 2251 | 2252 | err2: 2253 | fclose(self_p->fto_p); 2254 | 2255 | err1: 2256 | fclose(self_p->ffrom_p); 2257 | 2258 | return (res); 2259 | } 2260 | 2261 | static int file_io_cleanup(struct file_io_t *self_p) 2262 | { 2263 | int res; 2264 | int res2; 2265 | int res3; 2266 | 2267 | res = fclose(self_p->ffrom_p); 2268 | res2 = fclose(self_p->fto_p); 2269 | res3 = fclose(self_p->fpatch_p); 2270 | 2271 | if ((res != 0) || (res2 != 0) || (res3 != 0)) { 2272 | res = -DETOOLS_FILE_CLOSE_FAILED; 2273 | } 2274 | 2275 | return (res); 2276 | } 2277 | 2278 | static int file_io_read(FILE *file_p, uint8_t *buf_p, size_t size) 2279 | { 2280 | int res; 2281 | 2282 | res = 0; 2283 | 2284 | if (size > 0) { 2285 | if (fread(buf_p, size, 1, file_p) != 1) { 2286 | res = -DETOOLS_FILE_READ_FAILED; 2287 | } 2288 | } 2289 | 2290 | return (res); 2291 | } 2292 | 2293 | static int file_io_from_read(void *arg_p, uint8_t *buf_p, size_t size) 2294 | { 2295 | struct file_io_t *self_p; 2296 | 2297 | self_p = (struct file_io_t *)arg_p; 2298 | 2299 | return (file_io_read(self_p->ffrom_p, buf_p, size)); 2300 | } 2301 | 2302 | static int file_io_from_seek(void *arg_p, int offset) 2303 | { 2304 | struct file_io_t *self_p; 2305 | 2306 | self_p = (struct file_io_t *)arg_p; 2307 | 2308 | return (fseek(self_p->ffrom_p, offset, SEEK_CUR)); 2309 | } 2310 | 2311 | static int file_io_patch_read(void *arg_p, uint8_t *buf_p, size_t size) 2312 | { 2313 | struct file_io_t *self_p; 2314 | 2315 | self_p = (struct file_io_t *)arg_p; 2316 | 2317 | return (file_io_read(self_p->fpatch_p, buf_p, size)); 2318 | } 2319 | 2320 | static int file_io_to_write(void *arg_p, const uint8_t *buf_p, size_t size) 2321 | { 2322 | int res; 2323 | struct file_io_t *self_p; 2324 | 2325 | self_p = (struct file_io_t *)arg_p; 2326 | res = 0; 2327 | 2328 | if (size > 0) { 2329 | if (fwrite(buf_p, size, 1, self_p->fto_p) != 1) { 2330 | res = -DETOOLS_FILE_WRITE_FAILED; 2331 | } 2332 | } 2333 | 2334 | return (res); 2335 | } 2336 | 2337 | int detools_apply_patch_filenames(const char *from_p, 2338 | const char *patch_p, 2339 | const char *to_p) 2340 | { 2341 | int res; 2342 | struct file_io_t file_io; 2343 | size_t patch_size; 2344 | 2345 | res = file_io_init(&file_io, 2346 | from_p, 2347 | patch_p, 2348 | to_p, 2349 | &patch_size); 2350 | 2351 | if (res != 0) { 2352 | return (res); 2353 | } 2354 | 2355 | res = detools_apply_patch_callbacks(file_io_from_read, 2356 | file_io_from_seek, 2357 | file_io_patch_read, 2358 | patch_size, 2359 | file_io_to_write, 2360 | &file_io); 2361 | 2362 | if (res != 0) { 2363 | goto err1; 2364 | } 2365 | 2366 | return (file_io_cleanup(&file_io)); 2367 | 2368 | err1: 2369 | (void)file_io_cleanup(&file_io); 2370 | 2371 | return (res); 2372 | } 2373 | 2374 | struct in_place_file_io_t { 2375 | FILE *fmemory_p; 2376 | FILE *fpatch_p; 2377 | }; 2378 | 2379 | static int in_place_file_io_init(struct in_place_file_io_t *self_p, 2380 | const char *memory_p, 2381 | const char *patch_p, 2382 | size_t *patch_size_p) 2383 | { 2384 | int res; 2385 | FILE *file_p; 2386 | 2387 | res = -DETOOLS_FILE_OPEN_FAILED; 2388 | 2389 | /* Memory. */ 2390 | file_p = fopen(memory_p, "r+b"); 2391 | 2392 | if (file_p == NULL) { 2393 | return (res); 2394 | } 2395 | 2396 | self_p->fmemory_p = file_p; 2397 | 2398 | /* Patch. */ 2399 | file_p = fopen(patch_p, "rb"); 2400 | 2401 | if (file_p == NULL) { 2402 | goto err1; 2403 | } 2404 | 2405 | self_p->fpatch_p = file_p; 2406 | res = file_size(self_p->fpatch_p, patch_size_p); 2407 | 2408 | if (res != 0) { 2409 | goto err2; 2410 | } 2411 | 2412 | return (res); 2413 | 2414 | err2: 2415 | fclose(self_p->fpatch_p); 2416 | 2417 | err1: 2418 | fclose(self_p->fmemory_p); 2419 | 2420 | return (res); 2421 | } 2422 | 2423 | static int in_place_file_io_mem_read(void *arg_p, 2424 | void *dst_p, 2425 | uintptr_t src, 2426 | size_t size) 2427 | { 2428 | int res; 2429 | struct in_place_file_io_t *self_p; 2430 | 2431 | self_p = (struct in_place_file_io_t *)arg_p; 2432 | res = 0; 2433 | 2434 | if (size > 0) { 2435 | res = fseek(self_p->fmemory_p, (int)src, SEEK_SET); 2436 | 2437 | if (res != 0) { 2438 | return (-DETOOLS_FILE_SEEK_FAILED); 2439 | } 2440 | 2441 | if (fread(dst_p, size, 1, self_p->fmemory_p) != 1) { 2442 | res = -DETOOLS_FILE_READ_FAILED; 2443 | } 2444 | } 2445 | 2446 | return (res); 2447 | } 2448 | 2449 | static int in_place_file_io_mem_write(void *arg_p, 2450 | uintptr_t dst, 2451 | void *src_p, 2452 | size_t size) 2453 | { 2454 | int res; 2455 | struct in_place_file_io_t *self_p; 2456 | 2457 | self_p = (struct in_place_file_io_t *)arg_p; 2458 | res = 0; 2459 | 2460 | if (size > 0) { 2461 | res = fseek(self_p->fmemory_p, (int)dst, SEEK_SET); 2462 | 2463 | if (res != 0) { 2464 | return (-DETOOLS_FILE_SEEK_FAILED); 2465 | } 2466 | 2467 | if (fwrite(src_p, size, 1, self_p->fmemory_p) != 1) { 2468 | res = -DETOOLS_FILE_WRITE_FAILED; 2469 | } 2470 | } 2471 | 2472 | return (res); 2473 | } 2474 | 2475 | static int in_place_file_io_mem_erase(void *arg_p, uintptr_t addr, size_t size) 2476 | { 2477 | (void)arg_p; 2478 | (void)addr; 2479 | (void)size; 2480 | 2481 | return (0); 2482 | } 2483 | 2484 | static int in_place_file_io_cleanup(struct in_place_file_io_t *self_p) 2485 | { 2486 | int res; 2487 | int res2; 2488 | 2489 | res = fclose(self_p->fmemory_p); 2490 | res2 = fclose(self_p->fpatch_p); 2491 | 2492 | if ((res != 0) || (res2 != 0)) { 2493 | res = -DETOOLS_FILE_CLOSE_FAILED; 2494 | } 2495 | 2496 | return (res); 2497 | } 2498 | 2499 | int detools_apply_patch_in_place_filenames(const char *memory_p, 2500 | const char *patch_p, 2501 | detools_step_set_t step_set, 2502 | detools_step_get_t step_get) 2503 | { 2504 | int res; 2505 | struct in_place_file_io_t file_io; 2506 | size_t patch_size; 2507 | 2508 | res = in_place_file_io_init(&file_io, 2509 | memory_p, 2510 | patch_p, 2511 | &patch_size); 2512 | 2513 | if (res != 0) { 2514 | return (res); 2515 | } 2516 | 2517 | res = detools_apply_patch_in_place_callbacks(in_place_file_io_mem_read, 2518 | in_place_file_io_mem_write, 2519 | in_place_file_io_mem_erase, 2520 | step_set, 2521 | step_get, 2522 | file_io_patch_read, 2523 | patch_size, 2524 | &file_io); 2525 | 2526 | if (res != 0) { 2527 | goto err1; 2528 | } 2529 | 2530 | return (in_place_file_io_cleanup(&file_io)); 2531 | 2532 | err1: 2533 | (void)in_place_file_io_cleanup(&file_io); 2534 | 2535 | return (res); 2536 | } 2537 | 2538 | #endif 2539 | 2540 | const char *detools_error_as_string(int error) 2541 | { 2542 | if (error < 0) { 2543 | error *= -1; 2544 | } 2545 | 2546 | switch (error) { 2547 | 2548 | case DETOOLS_NOT_IMPLEMENTED: 2549 | return "Function not implemented."; 2550 | 2551 | case DETOOLS_NOT_DONE: 2552 | return "Not done."; 2553 | 2554 | case DETOOLS_BAD_PATCH_TYPE: 2555 | return "Bad patch type."; 2556 | 2557 | case DETOOLS_BAD_COMPRESSION: 2558 | return "Bad compression."; 2559 | 2560 | case DETOOLS_INTERNAL_ERROR: 2561 | return "Internal error."; 2562 | 2563 | case DETOOLS_LZMA_INIT: 2564 | return "LZMA init."; 2565 | 2566 | case DETOOLS_LZMA_DECODE: 2567 | return "LZMA decode."; 2568 | 2569 | case DETOOLS_OUT_OF_MEMORY: 2570 | return "Out of memory."; 2571 | 2572 | case DETOOLS_CORRUPT_PATCH: 2573 | return "Corrupt patch."; 2574 | 2575 | case DETOOLS_IO_FAILED: 2576 | return "Input/output failed."; 2577 | 2578 | case DETOOLS_ALREADY_DONE: 2579 | return "Already done."; 2580 | 2581 | case DETOOLS_FILE_OPEN_FAILED: 2582 | return "File open failed."; 2583 | 2584 | case DETOOLS_FILE_CLOSE_FAILED: 2585 | return "File close failed."; 2586 | 2587 | case DETOOLS_FILE_READ_FAILED: 2588 | return "File read failed."; 2589 | 2590 | case DETOOLS_FILE_WRITE_FAILED: 2591 | return "File write failed."; 2592 | 2593 | case DETOOLS_FILE_SEEK_FAILED: 2594 | return "File seek failed."; 2595 | 2596 | case DETOOLS_FILE_TELL_FAILED: 2597 | return "File tell failed."; 2598 | 2599 | case DETOOLS_SHORT_HEADER: 2600 | return "Short header."; 2601 | 2602 | case DETOOLS_NOT_ENOUGH_PATCH_DATA: 2603 | return "Not enough patch data."; 2604 | 2605 | case DETOOLS_HEATSHRINK_SINK: 2606 | return "Heatshrink sink."; 2607 | 2608 | case DETOOLS_HEATSHRINK_POLL: 2609 | return "Heatshrink poll."; 2610 | 2611 | case DETOOLS_STEP_SET_FAILED: 2612 | return "Step set failed."; 2613 | 2614 | case DETOOLS_STEP_GET_FAILED: 2615 | return "Step get failed."; 2616 | 2617 | case DETOOLS_ALREADY_FAILED: 2618 | return "Already failed."; 2619 | 2620 | case DETOOLS_CORRUPT_PATCH_OVERFLOW: 2621 | return "Corrupt patch, overflow."; 2622 | 2623 | case DETOOLS_CORRUPT_PATCH_CRLE_KIND: 2624 | return "Corrupt patch, CRLE kind."; 2625 | 2626 | case DETOOLS_HEATSHRINK_HEADER: 2627 | return "Heatshrink header."; 2628 | 2629 | default: 2630 | return "Unknown error."; 2631 | } 2632 | } 2633 | -------------------------------------------------------------------------------- /components/detools/heatshrink/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015, Scott Vokes 2 | All rights reserved. 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /components/detools/heatshrink/README.md: -------------------------------------------------------------------------------- 1 | # heatshrink 2 | 3 | A data compression/decompression library for embedded/real-time systems. 4 | 5 | 6 | ## Key Features: 7 | 8 | - **Low memory usage (as low as 50 bytes)** 9 | It is useful for some cases with less than 50 bytes, and useful 10 | for many general cases with < 300 bytes. 11 | - **Incremental, bounded CPU use** 12 | You can chew on input data in arbitrarily tiny bites. 13 | This is a useful property in hard real-time environments. 14 | - **Can use either static or dynamic memory allocation** 15 | The library doesn't impose any constraints on memory management. 16 | - **ISC license** 17 | You can use it freely, even for commercial purposes. 18 | 19 | 20 | ## Getting Started: 21 | 22 | There is a standalone command-line program, `heatshrink`, but the 23 | encoder and decoder can also be used as libraries, independent of each 24 | other. To do so, copy `heatshrink_common.h`, `heatshrink_config.h`, and 25 | either `heatshrink_encoder.c` or `heatshrink_decoder.c` (and their 26 | respective header) into your project. For projects that use both, 27 | static libraries are built that use static and dynamic allocation. 28 | 29 | Dynamic allocation is used by default, but in an embedded context, you 30 | probably want to statically allocate the encoder/decoder. Set 31 | `HEATSHRINK_DYNAMIC_ALLOC` to 0 in `heatshrink_config.h`. 32 | 33 | 34 | ### Basic Usage 35 | 36 | 1. Allocate a `heatshrink_encoder` or `heatshrink_decoder` state machine 37 | using their `alloc` function, or statically allocate one and call their 38 | `reset` function to initialize them. (See below for configuration 39 | options.) 40 | 41 | 2. Use `sink` to sink an input buffer into the state machine. The 42 | `input_size` pointer argument will be set to indicate how many bytes of 43 | the input buffer were actually consumed. (If 0 bytes were conusmed, the 44 | buffer is full.) 45 | 46 | 3. Use `poll` to move output from the state machine into an output 47 | buffer. The `output_size` pointer argument will be set to indicate how 48 | many bytes were output, and the function return value will indicate 49 | whether further output is available. (The state machine may not output 50 | any data until it has received enough input.) 51 | 52 | Repeat steps 2 and 3 to stream data through the state machine. Since 53 | it's doing data compression, the input and output sizes can vary 54 | significantly. Looping will be necessary to buffer the input and output 55 | as the data is processed. 56 | 57 | 4. When the end of the input stream is reached, call `finish` to notify 58 | the state machine that no more input is available. The return value from 59 | `finish` will indicate whether any output remains. if so, call `poll` to 60 | get more. 61 | 62 | Continue calling `finish` and `poll`ing to flush remaining output until 63 | `finish` indicates that the output has been exhausted. 64 | 65 | Sinking more data after `finish` has been called will not work without 66 | calling `reset` on the state machine. 67 | 68 | 69 | ## Configuration 70 | 71 | heatshrink has a couple configuration options, which impact its resource 72 | usage and how effectively it can compress data. These are set when 73 | dynamically allocating an encoder or decoder, or in `heatshrink_config.h` 74 | if they are statically allocated. 75 | 76 | - `window_sz2`, `-w` in the CLI: Set the window size to 2^W bytes. 77 | 78 | The window size determines how far back in the input can be searched for 79 | repeated patterns. A `window_sz2` of 8 will only use 256 bytes (2^8), 80 | while a `window_sz2` of 10 will use 1024 bytes (2^10). The latter uses 81 | more memory, but may also compress more effectively by detecting more 82 | repetition. 83 | 84 | The `window_sz2` setting currently must be between 4 and 15. 85 | 86 | - `lookahead_sz2`, `-l` in the CLI: Set the lookahead size to 2^L bytes. 87 | 88 | The lookahead size determines the max length for repeated patterns that 89 | are found. If the `lookahead_sz2` is 4, a 50-byte run of 'a' characters 90 | will be represented as several repeated 16-byte patterns (2^4 is 16), 91 | whereas a larger `lookahead_sz2` may be able to represent it all at 92 | once. The number of bits used for the lookahead size is fixed, so an 93 | overly large lookahead size can reduce compression by adding unused 94 | size bits to small patterns. 95 | 96 | The `lookahead_sz2` setting currently must be between 3 and the 97 | `window_sz2` - 1. 98 | 99 | - `input_buffer_size` - How large an input buffer to use for the 100 | decoder. This impacts how much work the decoder can do in a single 101 | step, and a larger buffer will use more memory. An extremely small 102 | buffer (say, 1 byte) will add overhead due to lots of suspend/resume 103 | function calls, but should not change how well data compresses. 104 | 105 | 106 | ### Recommended Defaults 107 | 108 | For embedded/low memory contexts, a `window_sz2` in the 8 to 10 range is 109 | probably a good default, depending on how tight memory is. Smaller or 110 | larger window sizes may make better trade-offs in specific 111 | circumstances, but should be checked with representative data. 112 | 113 | The `lookahead_sz2` should probably start near the `window_sz2`/2, e.g. 114 | -w 8 -l 4 or -w 10 -l 5. The command-line program can be used to measure 115 | how well test data works with different settings. 116 | 117 | 118 | ## More Information and Benchmarks: 119 | 120 | heatshrink is based on [LZSS], since it's particularly suitable for 121 | compression in small amounts of memory. It can use an optional, small 122 | [index] to make compression significantly faster, but otherwise can run 123 | in under 100 bytes of memory. The index currently adds 2^(window size+1) 124 | bytes to memory usage for compression, and temporarily allocates 512 125 | bytes on the stack during index construction (if the index is enabled). 126 | 127 | For more information, see the [blog post] for an overview, and the 128 | `heatshrink_encoder.h` / `heatshrink_decoder.h` header files for API 129 | documentation. 130 | 131 | [blog post]: http://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ 132 | [index]: http://spin.atomicobject.com/2014/01/13/lightweight-indexing-for-embedded-systems/ 133 | [LZSS]: http://en.wikipedia.org/wiki/Lempel-Ziv-Storer-Szymanski 134 | 135 | 136 | ## Build Status 137 | 138 | [![Build Status](https://travis-ci.org/atomicobject/heatshrink.png)](http://travis-ci.org/atomicobject/heatshrink) 139 | -------------------------------------------------------------------------------- /components/detools/heatshrink/heatshrink_common.h: -------------------------------------------------------------------------------- 1 | #ifndef HEATSHRINK_H 2 | #define HEATSHRINK_H 3 | 4 | #define HEATSHRINK_AUTHOR "Scott Vokes " 5 | #define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" 6 | 7 | /* Version 0.4.1 */ 8 | #define HEATSHRINK_VERSION_MAJOR 0 9 | #define HEATSHRINK_VERSION_MINOR 4 10 | #define HEATSHRINK_VERSION_PATCH 1 11 | 12 | #define HEATSHRINK_MIN_WINDOW_BITS 4 13 | #define HEATSHRINK_MAX_WINDOW_BITS 15 14 | 15 | #define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 16 | 17 | #define HEATSHRINK_LITERAL_MARKER 0x01 18 | #define HEATSHRINK_BACKREF_MARKER 0x00 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /components/detools/heatshrink/heatshrink_config.h: -------------------------------------------------------------------------------- 1 | #ifndef HEATSHRINK_CONFIG_H 2 | #define HEATSHRINK_CONFIG_H 3 | 4 | /* Should functionality assuming dynamic allocation be used? */ 5 | #ifndef HEATSHRINK_DYNAMIC_ALLOC 6 | #define HEATSHRINK_DYNAMIC_ALLOC 0 7 | #endif 8 | 9 | #if HEATSHRINK_DYNAMIC_ALLOC 10 | /* Optional replacement of malloc/free */ 11 | #define HEATSHRINK_MALLOC(SZ) malloc(SZ) 12 | #define HEATSHRINK_FREE(P, SZ) free(P) 13 | #else 14 | /* Required parameters for static configuration */ 15 | #define HEATSHRINK_STATIC_WINDOW_BITS 8 16 | #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE (1 << HEATSHRINK_STATIC_WINDOW_BITS) 17 | #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 7 18 | #endif 19 | 20 | /* Turn on logging for debugging. */ 21 | #define HEATSHRINK_DEBUGGING_LOGS 0 22 | 23 | /* Use indexing for faster compression. (This requires additional space.) */ 24 | #define HEATSHRINK_USE_INDEX 0 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /components/detools/heatshrink/heatshrink_decoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "heatshrink_decoder.h" 4 | 5 | /* States for the polling state machine. */ 6 | typedef enum { 7 | HSDS_TAG_BIT, /* tag bit */ 8 | HSDS_YIELD_LITERAL, /* ready to yield literal byte */ 9 | HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ 10 | HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ 11 | HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ 12 | HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ 13 | HSDS_YIELD_BACKREF, /* ready to yield back-reference */ 14 | } HSD_state; 15 | 16 | #if HEATSHRINK_DEBUGGING_LOGS 17 | #include 18 | #include 19 | #include 20 | #define LOG(...) fprintf(stderr, __VA_ARGS__) 21 | #define ASSERT(X) assert(X) 22 | static const char *state_names[] = { 23 | "tag_bit", 24 | "yield_literal", 25 | "backref_index_msb", 26 | "backref_index_lsb", 27 | "backref_count_msb", 28 | "backref_count_lsb", 29 | "yield_backref", 30 | }; 31 | #else 32 | #define LOG(...) /* no-op */ 33 | #define ASSERT(X) /* no-op */ 34 | #endif 35 | 36 | typedef struct { 37 | uint8_t *buf; /* output buffer */ 38 | size_t buf_size; /* buffer size */ 39 | size_t *output_size; /* bytes pushed to buffer, so far */ 40 | } output_info; 41 | 42 | #define NO_BITS ((uint16_t)-1) 43 | 44 | /* Forward references. */ 45 | static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); 46 | static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); 47 | 48 | #if HEATSHRINK_DYNAMIC_ALLOC 49 | heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, 50 | uint8_t window_sz2, 51 | uint8_t lookahead_sz2) 52 | { 53 | if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || 54 | (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || 55 | (input_buffer_size == 0) || 56 | (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || 57 | (lookahead_sz2 >= window_sz2)) { 58 | return NULL; 59 | } 60 | size_t buffers_sz = (1 << window_sz2) + input_buffer_size; 61 | size_t sz = sizeof(heatshrink_decoder) + buffers_sz; 62 | heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); 63 | if (hsd == NULL) { 64 | return NULL; 65 | } 66 | hsd->input_buffer_size = input_buffer_size; 67 | hsd->window_sz2 = window_sz2; 68 | hsd->lookahead_sz2 = lookahead_sz2; 69 | heatshrink_decoder_reset(hsd); 70 | LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", 71 | sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); 72 | return hsd; 73 | } 74 | 75 | void heatshrink_decoder_free(heatshrink_decoder *hsd) 76 | { 77 | size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; 78 | size_t sz = sizeof(heatshrink_decoder) + buffers_sz; 79 | HEATSHRINK_FREE(hsd, sz); 80 | (void)sz; /* may not be used by free */ 81 | } 82 | #endif 83 | 84 | void heatshrink_decoder_reset(heatshrink_decoder *hsd) 85 | { 86 | size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); 87 | size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); 88 | memset(hsd->buffers, 0, buf_sz + input_sz); 89 | hsd->state = HSDS_TAG_BIT; 90 | hsd->input_size = 0; 91 | hsd->input_index = 0; 92 | hsd->bit_index = 0x00; 93 | hsd->current_byte = 0x00; 94 | hsd->output_count = 0; 95 | hsd->output_index = 0; 96 | hsd->head_index = 0; 97 | } 98 | 99 | /* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ 100 | HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, 101 | uint8_t *in_buf, size_t size, size_t *input_size) 102 | { 103 | if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { 104 | return HSDR_SINK_ERROR_NULL; 105 | } 106 | 107 | size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; 108 | if (rem == 0) { 109 | *input_size = 0; 110 | return HSDR_SINK_FULL; 111 | } 112 | 113 | size = rem < size ? rem : size; 114 | LOG("-- sinking %zd bytes\n", size); 115 | /* copy into input buffer (at head of buffers) */ 116 | memcpy(&hsd->buffers[hsd->input_size], in_buf, size); 117 | hsd->input_size += size; 118 | *input_size = size; 119 | return HSDR_SINK_OK; 120 | } 121 | 122 | 123 | /***************** 124 | * Decompression * 125 | *****************/ 126 | 127 | #define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) 128 | #define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) 129 | 130 | // States 131 | static HSD_state st_tag_bit(heatshrink_decoder *hsd); 132 | static HSD_state st_yield_literal(heatshrink_decoder *hsd, 133 | output_info *oi); 134 | static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); 135 | static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); 136 | static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); 137 | static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); 138 | static HSD_state st_yield_backref(heatshrink_decoder *hsd, 139 | output_info *oi); 140 | 141 | HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, 142 | uint8_t *out_buf, size_t out_buf_size, size_t *output_size) 143 | { 144 | if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { 145 | return HSDR_POLL_ERROR_NULL; 146 | } 147 | *output_size = 0; 148 | 149 | output_info oi; 150 | oi.buf = out_buf; 151 | oi.buf_size = out_buf_size; 152 | oi.output_size = output_size; 153 | 154 | while (1) { 155 | LOG("-- poll, state is %d (%s), input_size %d\n", 156 | hsd->state, state_names[hsd->state], hsd->input_size); 157 | uint8_t in_state = hsd->state; 158 | switch (in_state) { 159 | case HSDS_TAG_BIT: 160 | hsd->state = st_tag_bit(hsd); 161 | break; 162 | case HSDS_YIELD_LITERAL: 163 | hsd->state = st_yield_literal(hsd, &oi); 164 | break; 165 | case HSDS_BACKREF_INDEX_MSB: 166 | hsd->state = st_backref_index_msb(hsd); 167 | break; 168 | case HSDS_BACKREF_INDEX_LSB: 169 | hsd->state = st_backref_index_lsb(hsd); 170 | break; 171 | case HSDS_BACKREF_COUNT_MSB: 172 | hsd->state = st_backref_count_msb(hsd); 173 | break; 174 | case HSDS_BACKREF_COUNT_LSB: 175 | hsd->state = st_backref_count_lsb(hsd); 176 | break; 177 | case HSDS_YIELD_BACKREF: 178 | hsd->state = st_yield_backref(hsd, &oi); 179 | break; 180 | default: 181 | return HSDR_POLL_ERROR_UNKNOWN; 182 | } 183 | 184 | /* If the current state cannot advance, check if input or output 185 | * buffer are exhausted. */ 186 | if (hsd->state == in_state) { 187 | if (*output_size == out_buf_size) { 188 | return HSDR_POLL_MORE; 189 | } 190 | return HSDR_POLL_EMPTY; 191 | } 192 | } 193 | } 194 | 195 | static HSD_state st_tag_bit(heatshrink_decoder *hsd) 196 | { 197 | uint32_t bits = get_bits(hsd, 1); // get tag bit 198 | if (bits == NO_BITS) { 199 | return HSDS_TAG_BIT; 200 | } else if (bits) { 201 | return HSDS_YIELD_LITERAL; 202 | } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { 203 | return HSDS_BACKREF_INDEX_MSB; 204 | } else { 205 | hsd->output_index = 0; 206 | return HSDS_BACKREF_INDEX_LSB; 207 | } 208 | } 209 | 210 | static HSD_state st_yield_literal(heatshrink_decoder *hsd, 211 | output_info *oi) 212 | { 213 | /* Emit a repeated section from the window buffer, and add it (again) 214 | * to the window buffer. (Note that the repetition can include 215 | * itself.)*/ 216 | if (*oi->output_size < oi->buf_size) { 217 | uint16_t byte = get_bits(hsd, 8); 218 | if (byte == NO_BITS) { 219 | return HSDS_YIELD_LITERAL; /* out of input */ 220 | } 221 | uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; 222 | uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; 223 | uint8_t c = byte & 0xFF; 224 | LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); 225 | buf[hsd->head_index++ & mask] = c; 226 | push_byte(hsd, oi, c); 227 | return HSDS_TAG_BIT; 228 | } else { 229 | return HSDS_YIELD_LITERAL; 230 | } 231 | } 232 | 233 | static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) 234 | { 235 | uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); 236 | ASSERT(bit_ct > 8); 237 | uint16_t bits = get_bits(hsd, bit_ct - 8); 238 | LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); 239 | if (bits == NO_BITS) { 240 | return HSDS_BACKREF_INDEX_MSB; 241 | } 242 | hsd->output_index = bits << 8; 243 | return HSDS_BACKREF_INDEX_LSB; 244 | } 245 | 246 | static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) 247 | { 248 | uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); 249 | uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); 250 | LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); 251 | if (bits == NO_BITS) { 252 | return HSDS_BACKREF_INDEX_LSB; 253 | } 254 | hsd->output_index |= bits; 255 | hsd->output_index++; 256 | uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); 257 | hsd->output_count = 0; 258 | return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; 259 | } 260 | 261 | static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) 262 | { 263 | uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); 264 | ASSERT(br_bit_ct > 8); 265 | uint16_t bits = get_bits(hsd, br_bit_ct - 8); 266 | LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); 267 | if (bits == NO_BITS) { 268 | return HSDS_BACKREF_COUNT_MSB; 269 | } 270 | hsd->output_count = bits << 8; 271 | return HSDS_BACKREF_COUNT_LSB; 272 | } 273 | 274 | static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) 275 | { 276 | uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); 277 | uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); 278 | LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); 279 | if (bits == NO_BITS) { 280 | return HSDS_BACKREF_COUNT_LSB; 281 | } 282 | hsd->output_count |= bits; 283 | hsd->output_count++; 284 | return HSDS_YIELD_BACKREF; 285 | } 286 | 287 | static HSD_state st_yield_backref(heatshrink_decoder *hsd, 288 | output_info *oi) 289 | { 290 | size_t count = oi->buf_size - *oi->output_size; 291 | if (count > 0) { 292 | size_t i = 0; 293 | if (hsd->output_count < count) { 294 | count = hsd->output_count; 295 | } 296 | uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; 297 | uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; 298 | uint16_t neg_offset = hsd->output_index; 299 | LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); 300 | ASSERT(neg_offset <= mask + 1); 301 | ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); 302 | 303 | for (i = 0; i < count; i++) { 304 | uint8_t c = buf[(hsd->head_index - neg_offset) & mask]; 305 | push_byte(hsd, oi, c); 306 | buf[hsd->head_index & mask] = c; 307 | hsd->head_index++; 308 | LOG(" -- ++ 0x%02x\n", c); 309 | } 310 | hsd->output_count -= count; 311 | if (hsd->output_count == 0) { 312 | return HSDS_TAG_BIT; 313 | } 314 | } 315 | return HSDS_YIELD_BACKREF; 316 | } 317 | 318 | /* Get the next COUNT bits from the input buffer, saving incremental progress. 319 | * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ 320 | static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) 321 | { 322 | uint16_t accumulator = 0; 323 | int i = 0; 324 | if (count > 15) { 325 | return NO_BITS; 326 | } 327 | LOG("-- popping %u bit(s)\n", count); 328 | 329 | /* If we aren't able to get COUNT bits, suspend immediately, because we 330 | * don't track how many bits of COUNT we've accumulated before suspend. */ 331 | if (hsd->input_size == 0) { 332 | if (hsd->bit_index < (1 << (count - 1))) { 333 | return NO_BITS; 334 | } 335 | } 336 | 337 | for (i = 0; i < count; i++) { 338 | if (hsd->bit_index == 0x00) { 339 | if (hsd->input_size == 0) { 340 | LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", 341 | accumulator, accumulator); 342 | return NO_BITS; 343 | } 344 | hsd->current_byte = hsd->buffers[hsd->input_index++]; 345 | LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); 346 | if (hsd->input_index == hsd->input_size) { 347 | hsd->input_index = 0; /* input is exhausted */ 348 | hsd->input_size = 0; 349 | } 350 | hsd->bit_index = 0x80; 351 | } 352 | accumulator <<= 1; 353 | if (hsd->current_byte & hsd->bit_index) { 354 | accumulator |= 0x01; 355 | if (0) { 356 | LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", 357 | accumulator, hsd->bit_index); 358 | } 359 | } else { 360 | if (0) { 361 | LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", 362 | accumulator, hsd->bit_index); 363 | } 364 | } 365 | hsd->bit_index >>= 1; 366 | } 367 | 368 | if (count > 1) { 369 | LOG(" -- accumulated %08x\n", accumulator); 370 | } 371 | return accumulator; 372 | } 373 | 374 | HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) 375 | { 376 | if (hsd == NULL) { 377 | return HSDR_FINISH_ERROR_NULL; 378 | } 379 | switch (hsd->state) { 380 | case HSDS_TAG_BIT: 381 | return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; 382 | 383 | /* If we want to finish with no input, but are in these states, it's 384 | * because the 0-bit padding to the last byte looks like a backref 385 | * marker bit followed by all 0s for index and count bits. */ 386 | case HSDS_BACKREF_INDEX_LSB: 387 | case HSDS_BACKREF_INDEX_MSB: 388 | case HSDS_BACKREF_COUNT_LSB: 389 | case HSDS_BACKREF_COUNT_MSB: 390 | return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; 391 | 392 | /* If the output stream is padded with 0xFFs (possibly due to being in 393 | * flash memory), also explicitly check the input size rather than 394 | * uselessly returning MORE but yielding 0 bytes when polling. */ 395 | case HSDS_YIELD_LITERAL: 396 | return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; 397 | 398 | default: 399 | return HSDR_FINISH_MORE; 400 | } 401 | } 402 | 403 | static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) 404 | { 405 | LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); 406 | oi->buf[(*oi->output_size)++] = byte; 407 | (void)hsd; 408 | } 409 | -------------------------------------------------------------------------------- /components/detools/heatshrink/heatshrink_decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef HEATSHRINK_DECODER_H 2 | #define HEATSHRINK_DECODER_H 3 | 4 | #include 5 | #include 6 | #include "heatshrink_common.h" 7 | #include "heatshrink_config.h" 8 | 9 | typedef enum { 10 | HSDR_SINK_OK, /* data sunk, ready to poll */ 11 | HSDR_SINK_FULL, /* out of space in internal buffer */ 12 | HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ 13 | } HSD_sink_res; 14 | 15 | typedef enum { 16 | HSDR_POLL_EMPTY, /* input exhausted */ 17 | HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ 18 | HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ 19 | HSDR_POLL_ERROR_UNKNOWN=-2, 20 | } HSD_poll_res; 21 | 22 | typedef enum { 23 | HSDR_FINISH_DONE, /* output is done */ 24 | HSDR_FINISH_MORE, /* more output remains */ 25 | HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ 26 | } HSD_finish_res; 27 | 28 | #if HEATSHRINK_DYNAMIC_ALLOC 29 | #define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ 30 | ((BUF)->input_buffer_size) 31 | #define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ 32 | ((BUF)->window_sz2) 33 | #define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ 34 | ((BUF)->lookahead_sz2) 35 | #else 36 | #define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ 37 | HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 38 | #define HEATSHRINK_DECODER_WINDOW_BITS(_) \ 39 | (HEATSHRINK_STATIC_WINDOW_BITS) 40 | #define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ 41 | (HEATSHRINK_STATIC_LOOKAHEAD_BITS) 42 | #endif 43 | 44 | typedef struct { 45 | uint16_t input_size; /* bytes in input buffer */ 46 | uint16_t input_index; /* offset to next unprocessed input byte */ 47 | uint16_t output_count; /* how many bytes to output */ 48 | uint16_t output_index; /* index for bytes to output */ 49 | uint16_t head_index; /* head of window buffer */ 50 | uint8_t state; /* current state machine node */ 51 | uint8_t current_byte; /* current byte of input */ 52 | uint8_t bit_index; /* current bit index */ 53 | 54 | #if HEATSHRINK_DYNAMIC_ALLOC 55 | /* Fields that are only used if dynamically allocated. */ 56 | uint8_t window_sz2; /* window buffer bits */ 57 | uint8_t lookahead_sz2; /* lookahead bits */ 58 | uint16_t input_buffer_size; /* input buffer size */ 59 | 60 | /* Input buffer, then expansion window buffer */ 61 | uint8_t buffers[]; 62 | #else 63 | /* Input buffer, then expansion window buffer */ 64 | uint8_t buffers[2 * HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; 65 | #endif 66 | } heatshrink_decoder; 67 | 68 | #if HEATSHRINK_DYNAMIC_ALLOC 69 | /* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, 70 | * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead 71 | * size of 2^lookahead_sz2. (The window buffer and lookahead sizes 72 | * must match the settings used when the data was compressed.) 73 | * Returns NULL on error. */ 74 | heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, 75 | uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); 76 | 77 | /* Free a decoder. */ 78 | void heatshrink_decoder_free(heatshrink_decoder *hsd); 79 | #endif 80 | 81 | /* Reset a decoder. */ 82 | void heatshrink_decoder_reset(heatshrink_decoder *hsd); 83 | 84 | /* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to 85 | * indicate how many bytes were actually sunk (in case a buffer was filled). */ 86 | HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, 87 | uint8_t *in_buf, size_t size, size_t *input_size); 88 | 89 | /* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into 90 | * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ 91 | HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, 92 | uint8_t *out_buf, size_t out_buf_size, size_t *output_size); 93 | 94 | /* Notify the dencoder that the input stream is finished. 95 | * If the return value is HSDR_FINISH_MORE, there is still more output, so 96 | * call heatshrink_decoder_poll and repeat. */ 97 | HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /components/detools/include/detools.h: -------------------------------------------------------------------------------- 1 | /** 2 | * BSD 2-Clause License 3 | * 4 | * Copyright (c) 2019-2020, Erik Moqvist 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef DETOOLS_H 30 | #define DETOOLS_H 31 | 32 | /* 33 | * Configuration. 34 | * 35 | * Define any of the defines below to 0 to disable given feature. 36 | */ 37 | 38 | #ifndef DETOOLS_CONFIG_FILE_IO 39 | # define DETOOLS_CONFIG_FILE_IO 0 40 | #endif 41 | 42 | #ifndef DETOOLS_CONFIG_COMPRESSION_NONE 43 | # define DETOOLS_CONFIG_COMPRESSION_NONE 0 44 | #endif 45 | 46 | #ifndef DETOOLS_CONFIG_COMPRESSION_LZMA 47 | # define DETOOLS_CONFIG_COMPRESSION_LZMA 0 48 | #endif 49 | 50 | #ifndef DETOOLS_CONFIG_COMPRESSION_CRLE 51 | # define DETOOLS_CONFIG_COMPRESSION_CRLE 0 52 | #endif 53 | 54 | #ifndef DETOOLS_CONFIG_COMPRESSION_HEATSHRINK 55 | # define DETOOLS_CONFIG_COMPRESSION_HEATSHRINK 1 56 | #endif 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | #define DETOOLS_VERSION "0.47.0" 64 | 65 | /* Error codes. */ 66 | #define DETOOLS_OK 0 67 | #define DETOOLS_NOT_IMPLEMENTED 1 68 | #define DETOOLS_NOT_DONE 2 69 | #define DETOOLS_BAD_PATCH_TYPE 3 70 | #define DETOOLS_BAD_COMPRESSION 4 71 | #define DETOOLS_INTERNAL_ERROR 5 72 | #define DETOOLS_LZMA_INIT 6 73 | #define DETOOLS_LZMA_DECODE 7 74 | #define DETOOLS_OUT_OF_MEMORY 8 75 | #define DETOOLS_CORRUPT_PATCH 9 76 | #define DETOOLS_IO_FAILED 10 77 | #define DETOOLS_ALREADY_DONE 11 78 | #define DETOOLS_FILE_OPEN_FAILED 12 79 | #define DETOOLS_FILE_CLOSE_FAILED 13 80 | #define DETOOLS_FILE_READ_FAILED 14 81 | #define DETOOLS_FILE_WRITE_FAILED 15 82 | #define DETOOLS_FILE_SEEK_FAILED 16 83 | #define DETOOLS_FILE_TELL_FAILED 17 84 | #define DETOOLS_SHORT_HEADER 18 85 | #define DETOOLS_NOT_ENOUGH_PATCH_DATA 19 86 | #define DETOOLS_HEATSHRINK_SINK 20 87 | #define DETOOLS_HEATSHRINK_POLL 21 88 | #define DETOOLS_STEP_SET_FAILED 22 89 | #define DETOOLS_STEP_GET_FAILED 23 90 | #define DETOOLS_ALREADY_FAILED 24 91 | #define DETOOLS_CORRUPT_PATCH_OVERFLOW 25 92 | #define DETOOLS_CORRUPT_PATCH_CRLE_KIND 26 93 | #define DETOOLS_HEATSHRINK_HEADER 27 94 | 95 | /** 96 | * Read callback. 97 | * 98 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 99 | * @param[out] buf_p Buffer to read into. 100 | * @param[in] size Number of bytes to read. 101 | * 102 | * @return zero(0) or negative error code. 103 | */ 104 | typedef int (*detools_read_t)(void *arg_p, uint8_t *buf_p, size_t size); 105 | 106 | /** 107 | * Write callback. 108 | * 109 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 110 | * @param[in] buf_p Buffer to write. 111 | * @param[in] size Number of bytes to write. 112 | * 113 | * @return zero(0) or negative error code. 114 | */ 115 | typedef int (*detools_write_t)(void *arg_p, const uint8_t *buf_p, size_t size); 116 | 117 | /** 118 | * Seek from current position callback. 119 | * 120 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 121 | * @param[in] offset Offset to seek to from current position. 122 | * 123 | * @return zero(0) or negative error code. 124 | */ 125 | typedef int (*detools_seek_t)(void *arg_p, int offset); 126 | 127 | /** 128 | * Memory read callback. 129 | * 130 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 131 | * @param[out] dst_p Buffer to read into. 132 | * @param[in] src Address to read from. 133 | * @param[in] size Number of bytes to read. 134 | * 135 | * @return zero(0) or negative error code. 136 | */ 137 | typedef int (*detools_mem_read_t)(void *arg_p, 138 | void *dst_p, 139 | uintptr_t src, 140 | size_t size); 141 | 142 | /** 143 | * Memory write callback. 144 | * 145 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 146 | * @param[in] dst Address to write to. 147 | * @param[in] addr src_p Buffer to write from. 148 | * @param[in] size Number of bytes to write. 149 | * 150 | * @return zero(0) or negative error code. 151 | */ 152 | typedef int (*detools_mem_write_t)(void *arg_p, 153 | uintptr_t dst, 154 | void *src_p, 155 | size_t size); 156 | 157 | /** 158 | * Memory erase callback. 159 | * 160 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 161 | * @param[in] addr Address to erase from. 162 | * @param[in] size Number of bytes to erase. 163 | * 164 | * @return zero(0) or negative error code. 165 | */ 166 | typedef int (*detools_mem_erase_t)(void *arg_p, uintptr_t addr, size_t size); 167 | 168 | /** 169 | * State read callback. 170 | * 171 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 172 | * @param[out] buf_p Buffer to read into. 173 | * @param[in] size Number of bytes to read. 174 | * 175 | * @return zero(0) or negative error code. 176 | */ 177 | typedef int (*detools_state_read_t)(void *arg_p, void *buf_p, size_t size); 178 | 179 | /** 180 | * State write callback. 181 | * 182 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 183 | * @param[in] buf_p Buffer to write. 184 | * @param[in] size Number of bytes to write. 185 | * 186 | * @return zero(0) or negative error code. 187 | */ 188 | typedef int (*detools_state_write_t)(void *arg_p, const void *buf_p, size_t size); 189 | 190 | /** 191 | * Step set callback. 192 | * 193 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 194 | * @param[in] step Step to set. Later read by the step get callback. 195 | * 196 | * @return zero(0) or negative error code. 197 | */ 198 | typedef int (*detools_step_set_t)(void *arg_p, int step); 199 | 200 | /** 201 | * Step get callback. 202 | * 203 | * @param[in] arg_p User data passed to detools_apply_patch_init(). 204 | * @param[out] step_p Outputs the most recently set step by the set 205 | * callback, or zero(0) if not yet set. 206 | * 207 | * @return zero(0) or negative error code. 208 | */ 209 | typedef int (*detools_step_get_t)(void *arg_p, int *step_p); 210 | 211 | struct detools_apply_patch_patch_reader_none_t { 212 | size_t patch_size; 213 | size_t patch_offset; 214 | }; 215 | 216 | #if DETOOLS_CONFIG_COMPRESSION_LZMA == 1 217 | 218 | #include 219 | 220 | struct detools_apply_patch_patch_reader_lzma_t { 221 | lzma_stream stream; 222 | uint8_t *input_p; 223 | uint8_t *output_p; 224 | size_t output_size; 225 | }; 226 | 227 | #endif 228 | 229 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 230 | 231 | #include "heatshrink_decoder.h" 232 | 233 | struct detools_apply_patch_patch_reader_heatshrink_t { 234 | int8_t window_sz2; 235 | int8_t lookahead_sz2; 236 | heatshrink_decoder decoder; 237 | }; 238 | 239 | #endif 240 | 241 | enum detools_unpack_usize_state_t { 242 | detools_unpack_usize_state_first_t = 0, 243 | detools_unpack_usize_state_consecutive_t 244 | }; 245 | 246 | struct detools_unpack_usize_t { 247 | enum detools_unpack_usize_state_t state; 248 | int value; 249 | int offset; 250 | }; 251 | 252 | enum detools_crle_state_t { 253 | detools_crle_state_idle_t = 0, 254 | detools_crle_state_scattered_size_t, 255 | detools_crle_state_scattered_data_t, 256 | detools_crle_state_repeated_repetitions_t, 257 | detools_crle_state_repeated_data_t, 258 | detools_crle_state_repeated_data_read_t 259 | }; 260 | 261 | struct detools_apply_patch_patch_reader_crle_t { 262 | enum detools_crle_state_t state; 263 | union { 264 | struct { 265 | size_t number_of_bytes_left; 266 | struct detools_unpack_usize_t size; 267 | } scattered; 268 | struct { 269 | uint8_t value; 270 | size_t number_of_bytes_left; 271 | struct detools_unpack_usize_t size; 272 | } repeated; 273 | } kind; 274 | }; 275 | 276 | struct detools_apply_patch_patch_reader_t { 277 | struct detools_apply_patch_chunk_t *patch_chunk_p; 278 | struct { 279 | int state; 280 | int value; 281 | int offset; 282 | bool is_signed; 283 | } size; 284 | union { 285 | #if DETOOLS_CONFIG_COMPRESSION_NONE == 1 286 | struct detools_apply_patch_patch_reader_none_t none; 287 | #endif 288 | #if DETOOLS_CONFIG_COMPRESSION_LZMA == 1 289 | struct detools_apply_patch_patch_reader_lzma_t lzma; 290 | #endif 291 | #if DETOOLS_CONFIG_COMPRESSION_CRLE == 1 292 | struct detools_apply_patch_patch_reader_crle_t crle; 293 | #endif 294 | #if DETOOLS_CONFIG_COMPRESSION_HEATSHRINK == 1 295 | struct detools_apply_patch_patch_reader_heatshrink_t heatshrink; 296 | #endif 297 | } compression; 298 | int (*destroy)(struct detools_apply_patch_patch_reader_t *self_p); 299 | int (*decompress)(struct detools_apply_patch_patch_reader_t *self_p, 300 | uint8_t *buf_p, 301 | size_t *size_p); 302 | }; 303 | 304 | struct detools_apply_patch_chunk_t { 305 | const uint8_t *buf_p; 306 | size_t size; 307 | size_t offset; 308 | }; 309 | 310 | enum detools_apply_patch_state_t { 311 | detools_apply_patch_state_init_t = 0, 312 | detools_apply_patch_state_dfpatch_size_t, 313 | detools_apply_patch_state_diff_size_t, 314 | detools_apply_patch_state_diff_data_t, 315 | detools_apply_patch_state_extra_size_t, 316 | detools_apply_patch_state_extra_data_t, 317 | detools_apply_patch_state_adjustment_t, 318 | detools_apply_patch_state_done_t, 319 | detools_apply_patch_state_failed_t 320 | }; 321 | 322 | /** 323 | * The apply patch data structure. 324 | */ 325 | struct detools_apply_patch_t { 326 | detools_read_t from_read; 327 | detools_seek_t from_seek; 328 | size_t patch_size; 329 | detools_write_t to_write; 330 | void *arg_p; 331 | enum detools_apply_patch_state_t state; 332 | int compression; 333 | size_t patch_offset; 334 | size_t to_offset; 335 | size_t to_size; 336 | int from_offset; 337 | size_t chunk_size; 338 | struct detools_apply_patch_patch_reader_t patch_reader; 339 | struct detools_apply_patch_chunk_t chunk; 340 | }; 341 | 342 | /** 343 | * The in-place apply patch data structure. 344 | */ 345 | struct detools_apply_patch_in_place_t { 346 | detools_mem_read_t mem_read; 347 | detools_mem_write_t mem_write; 348 | detools_mem_erase_t mem_erase; 349 | detools_step_set_t step_set; 350 | detools_step_get_t step_get; 351 | size_t patch_size; 352 | void *arg_p; 353 | enum detools_apply_patch_state_t state; 354 | int ongoing_step; 355 | size_t to_pos; 356 | size_t to_size; 357 | size_t segment_size; 358 | size_t shift_size; 359 | size_t chunk_size; 360 | struct { 361 | size_t index; 362 | int from_offset; 363 | size_t to_offset; 364 | size_t to_size; 365 | size_t to_pos; 366 | } segment; 367 | struct detools_apply_patch_patch_reader_t patch_reader; 368 | struct detools_apply_patch_chunk_t chunk; 369 | }; 370 | 371 | /** 372 | * Initialize given apply patch object. 373 | * 374 | * @param[out] self_p Apply patch object to initialize. 375 | * @param[in] from_read Callback to read from-data. 376 | * @param[in] from_seek Callback to seek from current position in from-data. 377 | * @param[in] patch_size Patch size in bytes. Not used if 378 | * `detools_apply_patch_restore()` is called 379 | * immediately after this function. 380 | * @param[in] to_write Destination callback. 381 | * @param[in] arg_p Argument passed to the callbacks. 382 | * 383 | * @return zero(0) or negative error code. 384 | */ 385 | int detools_apply_patch_init(struct detools_apply_patch_t *self_p, 386 | detools_read_t from_read, 387 | detools_seek_t from_seek, 388 | size_t patch_size, 389 | detools_write_t to_write, 390 | void *arg_p); 391 | 392 | /** 393 | * Dump given apply patch object state. Call 394 | * `detools_apply_patch_restore()` to restore an apply patch object to 395 | * the dumped state. 396 | * 397 | * @param[in] self_p Apply patch object to dump. 398 | * @param[in] write Write callback. 399 | * 400 | * @return zero(0) or negative error code. 401 | */ 402 | int detools_apply_patch_dump(struct detools_apply_patch_t *self_p, 403 | detools_state_write_t state_write); 404 | 405 | /** 406 | * Restore given apply patch object to given dumped 407 | * state. 408 | * 409 | * `detools_apply_patch_get_to_offset()` and 410 | * `detools_apply_patch_get_patch_offset()` are often called after 411 | * this function to restore the to and patch streams. 412 | * 413 | * @param[in,out] self_p Initialized apply patch object to restore. 414 | * @param[in] read Callback to read the dumped state. 415 | * 416 | * @return zero(0) or negative error code. 417 | */ 418 | int detools_apply_patch_restore(struct detools_apply_patch_t *self_p, 419 | detools_state_read_t state_read); 420 | 421 | /** 422 | * Get the current to stream offset. Often used to restore the to 423 | * stream after restore. 424 | * 425 | * @param[in] self_p Apply patch object. 426 | * 427 | * @return The current to stream offset. 428 | */ 429 | size_t detools_apply_patch_get_to_offset(struct detools_apply_patch_t *self_p); 430 | 431 | /** 432 | * Get the current patch stream offset. Often used to restore the 433 | * patch stream after restore. 434 | * 435 | * @param[in] self_p Apply patch object. 436 | * 437 | * @return The current patch stream offset. 438 | */ 439 | size_t detools_apply_patch_get_patch_offset(struct detools_apply_patch_t *self_p); 440 | 441 | /** 442 | * Call this function repeatedly until all patch data has been 443 | * processed or an error occurres. Call detools_apply_patch_finalize() 444 | * to finalize the patching, even if an error occurred. 445 | * 446 | * @param[in,out] self_p Initialized apply patch object. 447 | * @param[in] patch_p Next chunk of the patch. 448 | * @param[in] size Patch buffer size. 449 | * 450 | * @return zero(0) or negative error code. 451 | */ 452 | int detools_apply_patch_process(struct detools_apply_patch_t *self_p, 453 | const uint8_t *patch_p, 454 | size_t size); 455 | 456 | /** 457 | * Call once after all data has been processed to finalize the 458 | * patching. The value returned from this function should be ignored 459 | * if an error occurred in detools_apply_patch_process(). 460 | * 461 | * @param[in,out] self_p Initialized apply patch object. 462 | * 463 | * @return Size of to-data in bytes if the patch was applied 464 | * successfully, or negative error code. 465 | */ 466 | int detools_apply_patch_finalize(struct detools_apply_patch_t *self_p); 467 | 468 | /** 469 | * Initialize given in-place apply patch object. 470 | * 471 | * @param[out] self_p In-place apply patch object to initialize. 472 | * @param[in] mem_read Callback to read data. 473 | * @param[in] mem_write Callback to write data. 474 | * @param[in] mem_erase Callback to erase data. 475 | * @param[in] step_set Callback to set the step. 476 | * @param[in] step_get Callback to get the step. 477 | * @param[in] patch_size Patch size in bytes. 478 | * @param[in] arg_p Argument passed to the callbacks. 479 | * 480 | * @return zero(0) or negative error code. 481 | */ 482 | int detools_apply_patch_in_place_init( 483 | struct detools_apply_patch_in_place_t *self_p, 484 | detools_mem_read_t mem_read, 485 | detools_mem_write_t mem_write, 486 | detools_mem_erase_t mem_erase, 487 | detools_step_set_t step_set, 488 | detools_step_get_t step_get, 489 | size_t patch_size, 490 | void *arg_p); 491 | 492 | /** 493 | * Call this function repeatedly until all patch data has been 494 | * processed or an error occurres. Call 495 | * detools_apply_patch_in_place_finalize() to finalize the patching, 496 | * even if an error occurred. 497 | * 498 | * @param[in,out] self_p Initialized apply patch object. 499 | * @param[in] patch_p Next chunk of the patch. 500 | * @param[in] size Patch buffer size. 501 | * 502 | * @return zero(0) or negative error code. 503 | */ 504 | int detools_apply_patch_in_place_process( 505 | struct detools_apply_patch_in_place_t *self_p, 506 | const uint8_t *patch_p, 507 | size_t size); 508 | 509 | /** 510 | * Call once after all data has been processed to finalize the 511 | * patching. The value returned from this function should be ignored 512 | * if an error occurred in detools_apply_patch_in_place_process(). 513 | * 514 | * @param[in,out] self_p Initialized apply patch object. 515 | * 516 | * @return Size of to-data in bytes if the patch was applied 517 | * successfully, or negative error code. 518 | */ 519 | int detools_apply_patch_in_place_finalize( 520 | struct detools_apply_patch_in_place_t *self_p); 521 | 522 | /** 523 | * Apply given patch using read, write and seek callbacks. 524 | * 525 | * @param[in] from_read Source read callback. 526 | * @param[in] from_seek Source seek callback. 527 | * @param[in] patch_read Patch read callback. 528 | * @param[in] patch_size Patch size in bytes. 529 | * @param[in] to_write Destination write callback. 530 | * @param[in] arg_p Argument passed to all callbacks. 531 | * 532 | * @return Size of to-data in bytes or negative error code. 533 | */ 534 | int detools_apply_patch_callbacks(detools_read_t from_read, 535 | detools_seek_t from_seek, 536 | detools_read_t patch_read, 537 | size_t patch_size, 538 | detools_write_t to_write, 539 | void *arg_p); 540 | 541 | /** 542 | * Apply given in-place patch using read, write and erase callbacks. 543 | * 544 | * @param[in] mem_read Callback to read data. 545 | * @param[in] mem_write Callback to write data. 546 | * @param[in] mem_erase Callback to erase data. 547 | * @param[in] step_set Callback to set the step. 548 | * @param[in] step_get Callback to get the step. 549 | * @param[in] patch_read Patch read callback. 550 | * @param[in] patch_size Patch size in bytes. 551 | * @param[in] arg_p Argument passed to the callbacks. 552 | * 553 | * @return Size of to-data in bytes or negative error code. 554 | */ 555 | int detools_apply_patch_in_place_callbacks(detools_mem_read_t mem_read, 556 | detools_mem_write_t mem_write, 557 | detools_mem_erase_t mem_erase, 558 | detools_step_set_t step_set, 559 | detools_step_get_t step_get, 560 | detools_read_t patch_read, 561 | size_t patch_size, 562 | void *arg_p); 563 | 564 | #if DETOOLS_CONFIG_FILE_IO == 1 565 | 566 | /** 567 | * Apply given patch file to given from file and write the output to 568 | * given to file. 569 | * 570 | * @param[in] from_p Source file name. 571 | * @param[in] patch_p Patch file name. 572 | * @param[in] to_p Destination file name. 573 | * 574 | * @return Size of to-data in bytes or negative error code. 575 | */ 576 | int detools_apply_patch_filenames(const char *from_p, 577 | const char *patch_p, 578 | const char *to_p); 579 | 580 | /** 581 | * Apply given patch file to given memory file. 582 | * 583 | * @param[in] memory_p Memory file name. 584 | * @param[in] patch_p Patch file name. 585 | * @param[in] step_set Callback to set the step. 586 | * @param[in] step_get Callback to get the step. 587 | * 588 | * @return Size of to-data in bytes or negative error code. 589 | */ 590 | int detools_apply_patch_in_place_filenames(const char *memory_p, 591 | const char *patch_p, 592 | detools_step_set_t step_set, 593 | detools_step_get_t step_get); 594 | 595 | #endif 596 | 597 | /** 598 | * Get the error string for given error code. 599 | * 600 | * @param[in] Error code. 601 | * 602 | * @return Error string. 603 | */ 604 | const char *detools_error_as_string(int error); 605 | 606 | #endif 607 | -------------------------------------------------------------------------------- /examples/http_delta_ota/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | list(APPEND EXTRA_COMPONENT_DIRS "../../components/" $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) 5 | 6 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 7 | project(http_delta_ota) 8 | -------------------------------------------------------------------------------- /examples/http_delta_ota/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := http_delta_ota 7 | EXTRA_COMPONENT_DIRS += ../../components 8 | include $(IDF_PATH)/make/project.mk 9 | -------------------------------------------------------------------------------- /examples/http_delta_ota/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" 2 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /examples/http_delta_ota/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | -------------------------------------------------------------------------------- /examples/http_delta_ota/main/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/event_groups.h" 9 | 10 | #include "esp_system.h" 11 | #include "esp_log.h" 12 | #include "esp_err.h" 13 | #include "esp_event.h" 14 | 15 | #include "nvs_flash.h" 16 | #include "esp_wifi.h" 17 | #include "esp_netif.h" 18 | #include "protocol_examples_common.h" 19 | 20 | #include "esp_partition.h" 21 | 22 | #include "esp_http_server.h" 23 | #include "delta.h" 24 | 25 | #define HTTP_CHUNK_SIZE (2048) 26 | 27 | static const char *TAG = "http_delta_ota"; 28 | 29 | static void reboot(void) 30 | { 31 | ESP_LOGI(TAG, "Rebooting in 5 seconds..."); 32 | vTaskDelay(5000 / portTICK_PERIOD_MS); 33 | esp_restart(); 34 | } 35 | 36 | static esp_err_t ota_post_handler(httpd_req_t *req) 37 | { 38 | char *recv_buf = calloc(HTTP_CHUNK_SIZE, sizeof(char)); 39 | if (!recv_buf) { 40 | return ESP_FAIL; 41 | } 42 | 43 | int ret, content_length = req->content_len; 44 | ESP_LOGI(TAG, "Content length: %d B", content_length); 45 | 46 | int remaining = content_length; 47 | int64_t start = esp_timer_get_time(); 48 | 49 | delta_opts_t opts = INIT_DEFAULT_DELTA_OPTS(); 50 | 51 | delta_partition_writer_t writer; 52 | 53 | size_t count = 0; 54 | while (remaining > 0) { 55 | if ((ret = httpd_req_recv(req, recv_buf, MIN(remaining, HTTP_CHUNK_SIZE))) <= 0) { 56 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) { 57 | /* Retry receiving if timeout occurred */ 58 | continue; 59 | } 60 | goto ERROR; 61 | } 62 | 63 | if (count == 0 && delta_partition_init(&writer, opts.patch, content_length) != ESP_OK) { 64 | goto ERROR; 65 | } 66 | 67 | if (delta_partition_write(&writer, recv_buf, ret) != ESP_OK) { 68 | goto ERROR; 69 | }; 70 | 71 | count += ret; 72 | remaining -= ret; 73 | memset(recv_buf, 0x00, HTTP_CHUNK_SIZE); 74 | ESP_LOGI(TAG, "Download Progress: %0.2f %%", ((float)(content_length - remaining) / content_length) * 100); 75 | } 76 | 77 | free(recv_buf); 78 | 79 | ESP_LOGI(TAG, "Time taken to download patch: %0.3f s", (float)(esp_timer_get_time() - start) / 1000000L); 80 | ESP_LOGI(TAG, "Ready to apply patch..."); 81 | ESP_LOGI(TAG, "Patch size: %uKB", count/1024); 82 | 83 | ESP_LOGI(TAG, "---------------- detools ----------------"); 84 | int err = delta_check_and_apply(content_length, &opts); 85 | if (err) { 86 | ESP_LOGE(TAG, "Error: %s", delta_error_as_string(err)); 87 | goto ERROR; 88 | } 89 | 90 | httpd_resp_send(req, NULL, 0); 91 | ESP_ERROR_CHECK(example_disconnect()); 92 | reboot(); 93 | 94 | ERROR: 95 | httpd_resp_send_500(req); 96 | return ESP_OK; 97 | } 98 | 99 | static const httpd_uri_t ota = { 100 | .uri = "/ota", 101 | .method = HTTP_POST, 102 | .handler = ota_post_handler, 103 | .user_ctx = NULL 104 | }; 105 | 106 | static httpd_handle_t start_webserver(void) 107 | { 108 | httpd_handle_t server = NULL; 109 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 110 | config.lru_purge_enable = true; 111 | 112 | ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); 113 | if (httpd_start(&server, &config) == ESP_OK) { 114 | ESP_LOGI(TAG, "Registering URI handlers"); 115 | httpd_register_uri_handler(server, &ota); 116 | return server; 117 | } 118 | 119 | ESP_LOGI(TAG, "Error starting server!"); 120 | return NULL; 121 | } 122 | 123 | void app_main(void) 124 | { 125 | ESP_LOGI(TAG, "Initialising WiFi Connection..."); 126 | 127 | ESP_ERROR_CHECK(nvs_flash_init()); 128 | ESP_ERROR_CHECK(esp_netif_init()); 129 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 130 | 131 | /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. 132 | * Read "Establishing Wi-Fi or Ethernet Connection" section in 133 | * examples/protocols/README.md for more information about this function. 134 | */ 135 | ESP_ERROR_CHECK(example_connect()); 136 | 137 | 138 | /* Start the server for the first time */ 139 | ESP_LOGI(TAG, "Setting up HTTP server..."); 140 | start_webserver(); 141 | } 142 | -------------------------------------------------------------------------------- /examples/http_delta_ota/partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 20K 3 | otadata, data, ota, , 8K 4 | phy_init, data, phy, , 4K 5 | factory, app, factory, , 1024K 6 | ota_0, app, ota_0, , 1280K 7 | ota_1, app, ota_1, , 1280K 8 | patch, data, spiffs, , 256K 9 | -------------------------------------------------------------------------------- /examples/http_delta_ota/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 2 | CONFIG_PARTITION_TABLE_CUSTOM=y 3 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 4 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" --------------------------------------------------------------------------------