| /* |
| * Copyright (C) 2022-2023 The LineageOS Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <fcntl.h> |
| #include <libgen.h> |
| #include <unistd.h> |
| |
| #include <android-base/properties.h> |
| #include <edify/expr.h> |
| #include <liblp/liblp.h> |
| #include <otautil/error_code.h> |
| #include <ziparchive/zip_archive.h> |
| |
| Value *VerifyNoDowngradeFn(const char* name, State *state, |
| const std::vector<std::unique_ptr<Expr>>& argv) { |
| int ret = 1; |
| std::vector<std::string> args; |
| |
| if (argv.size() != 1 || !ReadArgs(state, argv, &args)) |
| return ErrorAbort(state, kArgsParsingFailure, |
| "%s() error parsing arguments", name); |
| |
| std::string blModel = android::base::GetProperty("ro.boot.em.model", ""); |
| std::string shortModel = blModel.substr(blModel.find("-") + 1, std::string::npos); |
| std::string blVer = android::base::GetProperty("ro.boot.bootloader", ""); |
| if (blVer.length() >= shortModel.length() + 4 |
| && args[0].length() >= shortModel.length() + 4) { // <model>XXU<binary> |
| std::string curBinary = blVer.substr(shortModel.length() + 3, 1); |
| std::string newBinary = args[0].substr(shortModel.length() + 3, 1); |
| if (newBinary.at(0) >= curBinary.at(0)) { |
| ret = 0; |
| } |
| } |
| |
| return StringValue(std::to_string(ret)); |
| } |
| |
| Value *MarkHeaderBtFn(const char* name, State *state, |
| const std::vector<std::unique_ptr<Expr>>& argv) { |
| int ret = 0; |
| std::vector<std::string> args; |
| const char* partition; |
| uint32_t magicOffset; |
| uint32_t numImages; |
| uint32_t magic1; |
| |
| if(argv.size() < 4 || argv.size() > 4 || !ReadArgs(state, argv, &args)) |
| return ErrorAbort(state, kArgsParsingFailure, |
| "%s() error parsing arguments", name); |
| |
| partition = args[0].c_str(); |
| magicOffset = std::atoi(args[1].c_str()); |
| numImages = std::atoi(args[2].c_str()); |
| magic1 = std::atoi(args[3].c_str()); |
| |
| int fd = open(partition, O_RDWR); |
| if (fd < 0) |
| return ErrorAbort(state, kFileOpenFailure, |
| "%s() failed to open %s", name, partition); |
| |
| magic1 = magic1 << (magicOffset * 8); |
| numImages = numImages << (magicOffset * 8); |
| write(fd, (char*)&magic1, sizeof(uint32_t)); |
| write(fd, (char*)&numImages, sizeof(uint32_t)); |
| close(fd); |
| |
| return StringValue(std::to_string(ret)); |
| } |
| |
| #define FILENAME_MAX_LEN 32 |
| |
| Value *WriteDataBtFn(const char* name, State *state, |
| const std::vector<std::unique_ptr<Expr>>& argv) { |
| int ret = 0; |
| std::vector<std::string> args; |
| const char* file; |
| const char* filename; |
| const char* partition; |
| uint32_t offset; |
| uint32_t filesize; |
| |
| if(argv.size() < 4 || argv.size() > 4 || !ReadArgs(state, argv, &args)) |
| return ErrorAbort(state, kArgsParsingFailure, |
| "%s() error parsing arguments", name); |
| |
| file = args[0].c_str(); |
| filename = basename(file); |
| partition = args[1].c_str(); |
| offset = std::atoi(args[2].c_str()); |
| filesize = std::atoi(args[3].c_str()); |
| |
| int fd = open(partition, O_RDWR); |
| if (fd < 0) |
| return ErrorAbort(state, kFileOpenFailure, |
| "%s() failed to open %s", name, partition); |
| |
| char filename_padded[FILENAME_MAX_LEN] = { 0 }; |
| strcpy(&filename_padded[0], filename); |
| |
| lseek(fd, offset, SEEK_SET); |
| write(fd, &filename_padded[0], FILENAME_MAX_LEN); |
| write(fd, (char*)&filesize, sizeof(uint32_t)); |
| |
| // write data |
| ZipArchiveHandle za = state->updater->GetPackageHandle(); |
| ZipEntry64 entry; |
| if (FindEntry(za, file, &entry) != 0) { |
| return ErrorAbort(state, kPackageExtractFileFailure, |
| "%s() %s not found in package", name, file); |
| } |
| |
| if (ExtractEntryToFile(za, &entry, fd)) |
| return ErrorAbort(state, kPackageExtractFileFailure, |
| "%s() failed to extract %s from package", name, file); |
| |
| close(fd); |
| |
| return StringValue(std::to_string(ret)); |
| } |
| |
| Value *RetrofitDynamicPartitionsFn(const char* /* name */, State *state, |
| const std::vector<std::unique_ptr<Expr>>& /* argv */) { |
| int ret = 0; |
| std::unique_ptr<android::fs_mgr::LpMetadata> pt; |
| std::string super_partition = android::base::GetProperty("ro.boot.super_partition", ""); |
| |
| if (super_partition.empty()) { |
| return ErrorAbort(state, kVendorFailure, |
| "The current recovery does not seem to support dynamic partitions, please update."); |
| } |
| |
| pt = android::fs_mgr::ReadMetadata(super_partition, 0); |
| if (!pt) { // No dynamic partitions metadata yet - flashing initial metadata is required |
| state->updater->UiPrint("Flashing initial dynamic partitions partition table..."); |
| |
| pt = android::fs_mgr::ReadFromImageFile("/tmp/super_empty.img"); |
| if (!pt) { |
| return ErrorAbort(state, kFileOpenFailure, "Failed to read /tmp/super_empty.img"); |
| } |
| |
| if (!android::fs_mgr::FlashPartitionTable(super_partition, *pt.get())) { |
| return ErrorAbort(state, kSetMetadataFailure, |
| "Failed to flash partition table of super partition: %s.", super_partition.c_str()); |
| } |
| } |
| |
| return StringValue(std::to_string(ret)); |
| } |
| |
| void Register_librecovery_updater_exynos9820() { |
| RegisterFunction("exynos9820.verify_no_downgrade", VerifyNoDowngradeFn); |
| RegisterFunction("exynos9820.mark_header_bt", MarkHeaderBtFn); |
| RegisterFunction("exynos9820.write_data_bt", WriteDataBtFn); |
| RegisterFunction("exynos9820.retrofit_dynamic_partitions", RetrofitDynamicPartitionsFn); |
| } |