1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
/*
* Copyright (C) 2020 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.
*/
#define TRACE_TAG INCREMENTAL
#include "incremental_utils.h"
#include <android-base/mapped_file.h>
#include <android-base/strings.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
#include <array>
#include <cinttypes>
#include <numeric>
#include <unordered_set>
#include "adb_trace.h"
#include "sysdeps.h"
using namespace std::literals;
static constexpr int kBlockSize = 4096;
static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
return (offset & ~(kBlockSize - 1)) >> 12;
}
template <class T>
T valueAt(int fd, off64_t offset) {
T t;
memset(&t, 0, sizeof(T));
if (adb_pread(fd, &t, sizeof(T), offset) != sizeof(T)) {
memset(&t, -1, sizeof(T));
}
return t;
}
static void appendBlocks(int32_t start, int count, std::vector<int32_t>* blocks) {
if (count == 1) {
blocks->push_back(start);
} else {
auto oldSize = blocks->size();
blocks->resize(oldSize + count);
std::iota(blocks->begin() + oldSize, blocks->end(), start);
}
}
template <class T>
static void unduplicate(std::vector<T>& v) {
std::unordered_set<T> uniques(v.size());
v.erase(std::remove_if(v.begin(), v.end(),
[&uniques](T t) { return !uniques.insert(t).second; }),
v.end());
}
static off64_t CentralDirOffset(int fd, int64_t fileSize) {
static constexpr int kZipEocdRecMinSize = 22;
static constexpr int32_t kZipEocdRecSig = 0x06054b50;
static constexpr int kZipEocdCentralDirSizeFieldOffset = 12;
static constexpr int kZipEocdCommentLengthFieldOffset = 20;
int32_t sigBuf = 0;
off64_t eocdOffset = -1;
off64_t maxEocdOffset = fileSize - kZipEocdRecMinSize;
int16_t commentLenBuf = 0;
// Search from the end of zip, backward to find beginning of EOCD
for (int16_t commentLen = 0; commentLen < fileSize; ++commentLen) {
sigBuf = valueAt<int32_t>(fd, maxEocdOffset - commentLen);
if (sigBuf == kZipEocdRecSig) {
commentLenBuf = valueAt<int16_t>(
fd, maxEocdOffset - commentLen + kZipEocdCommentLengthFieldOffset);
if (commentLenBuf == commentLen) {
eocdOffset = maxEocdOffset - commentLen;
break;
}
}
}
if (eocdOffset < 0) {
return -1;
}
off64_t cdLen = static_cast<int64_t>(
valueAt<int32_t>(fd, eocdOffset + kZipEocdCentralDirSizeFieldOffset));
return eocdOffset - cdLen;
}
// Does not support APKs larger than 4GB
static off64_t SignerBlockOffset(int fd, int64_t fileSize) {
static constexpr int kApkSigBlockMinSize = 32;
static constexpr int kApkSigBlockFooterSize = 24;
static constexpr int64_t APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42l;
static constexpr int64_t APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041l;
off64_t cdOffset = CentralDirOffset(fd, fileSize);
if (cdOffset < 0) {
return -1;
}
// CD offset is where original signer block ends. Search backwards for magic and footer.
if (cdOffset < kApkSigBlockMinSize ||
valueAt<int64_t>(fd, cdOffset - 2 * sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_LO ||
valueAt<int64_t>(fd, cdOffset - sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_HI) {
return -1;
}
int32_t signerSizeInFooter = valueAt<int32_t>(fd, cdOffset - kApkSigBlockFooterSize);
off64_t signerBlockOffset = cdOffset - signerSizeInFooter - sizeof(int64_t);
if (signerBlockOffset < 0) {
return -1;
}
int32_t signerSizeInHeader = valueAt<int32_t>(fd, signerBlockOffset);
if (signerSizeInFooter != signerSizeInHeader) {
return -1;
}
return signerBlockOffset;
}
static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, int64_t fileSize) {
int32_t signerBlockIndex = offsetToBlockIndex(signerBlockOffset);
int32_t lastBlockIndex = offsetToBlockIndex(fileSize);
const auto numPriorityBlocks = lastBlockIndex - signerBlockIndex + 1;
std::vector<int32_t> zipPriorityBlocks;
// Some magic here: most of zip libraries perform a scan for EOCD record starting at the offset
// of a maximum comment size from the end of the file. This means the last 65-ish KBs will be
// accessed first, followed by the rest of the central directory blocks. Make sure we
// send the data in the proper order, as central directory can be quite big by itself.
static constexpr auto kMaxZipCommentSize = 64 * 1024;
static constexpr auto kNumBlocksInEocdSearch = kMaxZipCommentSize / kBlockSize + 1;
if (numPriorityBlocks > kNumBlocksInEocdSearch) {
appendBlocks(lastBlockIndex - kNumBlocksInEocdSearch + 1, kNumBlocksInEocdSearch,
&zipPriorityBlocks);
appendBlocks(signerBlockIndex, numPriorityBlocks - kNumBlocksInEocdSearch,
&zipPriorityBlocks);
} else {
appendBlocks(signerBlockIndex, numPriorityBlocks, &zipPriorityBlocks);
}
// Somehow someone keeps accessing the start of the archive, even if there's nothing really
// interesting there...
appendBlocks(0, 1, &zipPriorityBlocks);
return zipPriorityBlocks;
}
[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(int fd) {
bool transferFdOwnership = false;
#ifdef _WIN32
//
// Need to create a special CRT FD here as the current one is not compatible with
// normal read()/write() calls that libziparchive uses.
// To make this work we have to create a copy of the file handle, as CRT doesn't care
// and closes it together with the new descriptor.
//
// Note: don't move this into a helper function, it's better to be hard to reuse because
// the code is ugly and won't work unless it's a last resort.
//
auto handle = adb_get_os_handle(fd);
HANDLE dupedHandle;
if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &dupedHandle, 0,
false, DUPLICATE_SAME_ACCESS)) {
D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
return {};
}
fd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
if (fd < 0) {
D("%s failed at _open_osfhandle: %d", __func__, errno);
::CloseHandle(handle);
return {};
}
transferFdOwnership = true;
#endif
ZipArchiveHandle zip;
if (OpenArchiveFd(fd, "apk_fd", &zip, transferFdOwnership) != 0) {
D("%s failed at OpenArchiveFd: %d", __func__, errno);
#ifdef _WIN32
// "_close()" is a secret WinCRT name for the regular close() function.
_close(fd);
#endif
return {};
}
return zip;
}
static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
int fd, int64_t fileSize) {
#ifndef __LP64__
if (fileSize >= INT_MAX) {
return {openZipArchiveFd(fd), nullptr};
}
#endif
auto mapping =
android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), 0, fileSize, PROT_READ);
if (!mapping) {
D("%s failed at FromOsHandle: %d", __func__, errno);
return {};
}
ZipArchiveHandle zip;
if (OpenArchiveFromMemory(mapping->data(), mapping->size(), "apk_mapping", &zip) != 0) {
D("%s failed at OpenArchiveFromMemory: %d", __func__, errno);
return {};
}
return {zip, std::move(mapping)};
}
static std::vector<int32_t> InstallationPriorityBlocks(int fd, int64_t fileSize) {
static constexpr std::array<std::string_view, 3> additional_matches = {
"resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
auto [zip, _] = openZipArchive(fd, fileSize);
if (!zip) {
return {};
}
auto matcher = [](std::string_view entry_name) {
if (entry_name.starts_with("lib/"sv) && entry_name.ends_with(".so"sv)) {
return true;
}
return std::any_of(additional_matches.begin(), additional_matches.end(),
[entry_name](std::string_view i) { return i == entry_name; });
};
void* cookie = nullptr;
if (StartIteration(zip, &cookie, std::move(matcher)) != 0) {
D("%s failed at StartIteration: %d", __func__, errno);
return {};
}
std::vector<int32_t> installationPriorityBlocks;
ZipEntry entry;
std::string_view entryName;
while (Next(cookie, &entry, &entryName) == 0) {
if (entryName == "classes.dex"sv) {
// Only the head is needed for installation
int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
appendBlocks(startBlockIndex, 1, &installationPriorityBlocks);
D("\tadding to priority blocks: '%.*s' 1", (int)entryName.size(), entryName.data());
} else {
// Full entries are needed for installation
off64_t entryStartOffset = entry.offset;
off64_t entryEndOffset =
entryStartOffset +
(entry.method == kCompressStored ? entry.uncompressed_length
: entry.compressed_length) +
(entry.has_data_descriptor ? 16 /* sizeof(DataDescriptor) */ : 0);
int32_t startBlockIndex = offsetToBlockIndex(entryStartOffset);
int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
numNewBlocks);
}
}
EndIteration(cookie);
CloseArchive(zip);
return installationPriorityBlocks;
}
namespace incremental {
std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, int fd, int64_t fileSize) {
if (!android::base::EndsWithIgnoreCase(filepath, ".apk")) {
return {};
}
off64_t signerOffset = SignerBlockOffset(fd, fileSize);
if (signerOffset < 0) {
// No signer block? not a valid APK
return {};
}
std::vector<int32_t> priorityBlocks = ZipPriorityBlocks(signerOffset, fileSize);
std::vector<int32_t> installationPriorityBlocks = InstallationPriorityBlocks(fd, fileSize);
priorityBlocks.insert(priorityBlocks.end(), installationPriorityBlocks.begin(),
installationPriorityBlocks.end());
unduplicate(priorityBlocks);
return priorityBlocks;
}
} // namespace incremental
|