| /* |
| * Copyright (C) 2019 The Android Open Source 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 "patch_utils.h" |
| |
| #include <stdio.h> |
| |
| #include "adb_io.h" |
| #include "adb_utils.h" |
| #include "android-base/endian.h" |
| #include "sysdeps.h" |
| |
| #include "apk_archive.h" |
| |
| using namespace com::android; |
| using namespace com::android::fastdeploy; |
| using namespace android::base; |
| |
| static constexpr char kSignature[] = "FASTDEPLOY"; |
| |
| APKMetaData PatchUtils::GetDeviceAPKMetaData(const APKDump& apk_dump) { |
| APKMetaData apkMetaData; |
| apkMetaData.set_absolute_path(apk_dump.absolute_path()); |
| |
| std::string md5Hash; |
| int64_t localFileHeaderOffset; |
| int64_t dataSize; |
| |
| const auto& cd = apk_dump.cd(); |
| auto cur = cd.data(); |
| int64_t size = cd.size(); |
| while (auto consumed = ApkArchive::ParseCentralDirectoryRecord( |
| cur, size, &md5Hash, &localFileHeaderOffset, &dataSize)) { |
| cur += consumed; |
| size -= consumed; |
| |
| auto apkEntry = apkMetaData.add_entries(); |
| apkEntry->set_md5(md5Hash); |
| apkEntry->set_dataoffset(localFileHeaderOffset); |
| apkEntry->set_datasize(dataSize); |
| } |
| return apkMetaData; |
| } |
| |
| APKMetaData PatchUtils::GetHostAPKMetaData(const char* apkPath) { |
| ApkArchive archive(apkPath); |
| auto dump = archive.ExtractMetadata(); |
| if (dump.cd().empty()) { |
| fprintf(stderr, "adb: Could not extract Central Directory from %s\n", apkPath); |
| error_exit("Aborting"); |
| } |
| |
| auto apkMetaData = GetDeviceAPKMetaData(dump); |
| |
| // Now let's set data sizes. |
| for (auto& apkEntry : *apkMetaData.mutable_entries()) { |
| auto dataSize = |
| archive.CalculateLocalFileEntrySize(apkEntry.dataoffset(), apkEntry.datasize()); |
| if (dataSize == 0) { |
| error_exit("Aborting"); |
| } |
| apkEntry.set_datasize(dataSize); |
| } |
| |
| return apkMetaData; |
| } |
| |
| void PatchUtils::WriteSignature(borrowed_fd output) { |
| WriteFdExactly(output, kSignature, sizeof(kSignature) - 1); |
| } |
| |
| void PatchUtils::WriteLong(int64_t value, borrowed_fd output) { |
| int64_t littleEndian = htole64(value); |
| WriteFdExactly(output, &littleEndian, sizeof(littleEndian)); |
| } |
| |
| void PatchUtils::WriteString(const std::string& value, android::base::borrowed_fd output) { |
| WriteLong(value.size(), output); |
| WriteFdExactly(output, value); |
| } |
| |
| void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) { |
| constexpr static size_t BUFFER_SIZE = 128 * 1024; |
| char buffer[BUFFER_SIZE]; |
| size_t transferAmount = 0; |
| while (transferAmount != amount) { |
| auto chunkAmount = std::min(amount - transferAmount, BUFFER_SIZE); |
| auto readAmount = adb_read(input, buffer, chunkAmount); |
| if (readAmount < 0) { |
| fprintf(stderr, "adb: failed to read from input: %s\n", strerror(errno)); |
| error_exit("Aborting"); |
| } |
| WriteFdExactly(output, buffer, readAmount); |
| transferAmount += readAmount; |
| } |
| } |