blob: 946fcc03f57e087299525dbd5c450b3de0fceff2 [file] [log] [blame]
Adam Lesinski7ad11102016-10-28 16:39:15 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski7ad11102016-10-28 16:39:15 -070017#include "androidfw/ApkAssets.h"
18
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080019#include <algorithm>
20
Adam Lesinski970bd8d2017-09-25 13:21:55 -070021#include "android-base/errors.h"
22#include "android-base/file.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070023#include "android-base/logging.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070024#include "android-base/unique_fd.h"
25#include "android-base/utf8.h"
26#include "utils/Compat.h"
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080027#include "utils/FileMap.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070028#include "ziparchive/zip_archive.h"
29
30#include "androidfw/Asset.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070031#include "androidfw/Idmap.h"
Winsonb0085ce2019-02-19 12:48:22 -080032#include "androidfw/misc.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070033#include "androidfw/ResourceTypes.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070034#include "androidfw/Util.h"
35
36namespace android {
37
Adam Lesinski970bd8d2017-09-25 13:21:55 -070038using base::SystemErrorCodeToString;
39using base::unique_fd;
40
41static const std::string kResourcesArsc("resources.arsc");
42
Winsonb0085ce2019-02-19 12:48:22 -080043ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070044 std::string path,
Winson9947f1e2019-08-16 10:20:39 -070045 time_t last_mod_time,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080046 package_property_t property_flags)
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070047 : zip_handle_(unmanaged_handle, ::CloseArchive),
48 path_(std::move(path)),
49 last_mod_time_(last_mod_time),
Ryan Mitchell73bfe412019-11-12 16:22:04 -080050 property_flags_(property_flags) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -070051}
Adam Lesinski03ebac82017-09-25 13:10:14 -070052
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070053std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path,
54 const package_property_t flags) {
55 ::ZipArchiveHandle unmanaged_handle;
56 const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
57 if (result != 0) {
58 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
59 ::CloseArchive(unmanaged_handle);
60 return {};
61 }
62
63 return LoadImpl(unmanaged_handle, path, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
64 flags);
Adam Lesinskida431a22016-12-29 16:08:16 -050065}
66
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070067std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
68 const std::string& friendly_name,
69 const package_property_t flags,
70 const off64_t offset,
71 const off64_t length) {
72 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
73 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
74 << kUnknownLength;
75
76 ::ZipArchiveHandle unmanaged_handle;
77 const int32_t result = (length == kUnknownLength)
78 ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
79 : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
80 offset);
81
82 if (result != 0) {
83 LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
84 << " and length " << length << ": " << ::ErrorCodeString(result);
85 ::CloseArchive(unmanaged_handle);
86 return {};
87 }
88
89 return LoadImpl(unmanaged_handle, friendly_name, nullptr /*idmap_asset*/,
90 nullptr /*loaded_idmap*/, flags);
91}
92
93std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(const std::string& path,
94 const package_property_t flags) {
95 auto resources_asset = CreateAssetFromFile(path);
96 if (!resources_asset) {
97 LOG(ERROR) << "Failed to open ARSC '" << path;
98 return {};
99 }
100
101 return LoadTableImpl(std::move(resources_asset), path, flags);
102}
103
104std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(unique_fd fd,
105 const std::string& friendly_name,
106 const package_property_t flags,
107 const off64_t offset,
108 const off64_t length) {
109 auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
110 if (!resources_asset) {
111 LOG(ERROR) << "Failed to open ARSC '" << friendly_name << "' through FD with offset " << offset
112 << " and length " << length;
113 return {};
114 }
115
116 return LoadTableImpl(std::move(resources_asset), friendly_name, flags);
Adam Lesinskida431a22016-12-29 16:08:16 -0500117}
118
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700119std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700120 const package_property_t flags) {
121 CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
122
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700123 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
124 if (idmap_asset == nullptr) {
125 return {};
126 }
127
128 const StringPiece idmap_data(
129 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
130 static_cast<size_t>(idmap_asset->getLength()));
131 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data);
132 if (loaded_idmap == nullptr) {
133 LOG(ERROR) << "failed to load IDMAP " << idmap_path;
134 return {};
135 }
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800136
Adam Lesinski441500b2017-11-13 17:52:25 -0800137
Adam Lesinski7ad11102016-10-28 16:39:15 -0700138 ::ZipArchiveHandle unmanaged_handle;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700139 auto overlay_path = loaded_idmap->OverlayApkPath();
140 const int32_t result = ::OpenArchive(overlay_path.c_str(), &unmanaged_handle);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700141 if (result != 0) {
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700142 LOG(ERROR) << "Failed to open overlay APK '" << overlay_path << "' "
143 << ::ErrorCodeString(result);
Songchun Fan898b3162019-07-08 09:00:34 -0700144 ::CloseArchive(unmanaged_handle);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700145 return {};
146 }
147
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700148 return LoadImpl(unmanaged_handle, overlay_path, std::move(idmap_asset), std::move(loaded_idmap),
149 flags | PROPERTY_OVERLAY);
150}
151
152std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(const package_property_t flags) {
153 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, "empty", -1, flags));
154 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
155 // Need to force a move for mingw32.
156 return std::move(loaded_apk);
157}
158
159std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
160 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
161 if (!fd.ok()) {
162 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
163 return {};
164 }
165
166 return CreateAssetFromFd(std::move(fd), path.c_str());
167}
168
169std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
170 const char* path,
171 off64_t offset,
172 off64_t length) {
173 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
174 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
175 << kUnknownLength;
176 if (length == kUnknownLength) {
177 length = lseek64(fd, 0, SEEK_END);
178 if (length < 0) {
179 LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
180 << SystemErrorCodeToString(errno);
181 return {};
182 }
183 }
184
185 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
186 if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
187 LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
188 << SystemErrorCodeToString(errno);
189 return {};
190 }
191
192 // If `path` is set, do not pass ownership of the `fd` to the new Asset since
193 // Asset::openFileDescriptor can use `path` to create new file descriptors.
194 return Asset::createFromUncompressedMap(std::move(file_map),
195 (path) ? base::unique_fd(-1) : std::move(fd),
196 Asset::AccessMode::ACCESS_RANDOM);
197}
198
199std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(ZipArchiveHandle unmanaged_handle,
200 const std::string& path,
201 std::unique_ptr<Asset> idmap_asset,
202 std::unique_ptr<const LoadedIdmap> idmap,
203 package_property_t property_flags) {
204 const time_t last_mod_time = getFileModDate(path.c_str());
Winsonb0085ce2019-02-19 12:48:22 -0800205
Adam Lesinski7ad11102016-10-28 16:39:15 -0700206 // Wrap the handle in a unique_ptr so it gets automatically closed.
Winson9947f1e2019-08-16 10:20:39 -0700207 std::unique_ptr<ApkAssets>
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800208 loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700209
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700210 // Find the resource table.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700211 ::ZipEntry entry;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700212 int32_t result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700213 if (result != 0) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700214 // There is no resources.arsc, so create an empty LoadedArsc and return.
215 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
216 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700217 }
218
219 if (entry.method == kCompressDeflated) {
Ryan Mitchell31b11052019-06-13 13:47:26 -0700220 ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700221 }
222
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700223 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
224 loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700225 if (loaded_apk->resources_asset_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700226 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700227 return {};
228 }
229
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700230 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
231 loaded_apk->idmap_asset_ = std::move(idmap_asset);
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700232 loaded_apk->loaded_idmap_ = std::move(idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700233
234 const StringPiece data(
235 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
236 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800237 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
238 property_flags);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700239 if (loaded_apk->loaded_arsc_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700240 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700241 return {};
242 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800243
244 // Need to force a move for mingw32.
245 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700246}
247
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700248std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(std::unique_ptr<Asset> resources_asset,
249 const std::string& path,
250 package_property_t property_flags) {
251 const time_t last_mod_time = getFileModDate(path.c_str());
Winson9947f1e2019-08-16 10:20:39 -0700252
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800253 std::unique_ptr<ApkAssets> loaded_apk(
254 new ApkAssets(nullptr, path, last_mod_time, property_flags));
Winson9947f1e2019-08-16 10:20:39 -0700255 loaded_apk->resources_asset_ = std::move(resources_asset);
256
257 const StringPiece data(
258 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
259 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800260 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
Winson9947f1e2019-08-16 10:20:39 -0700261 if (loaded_apk->loaded_arsc_ == nullptr) {
262 LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
263 return {};
264 }
265
266 // Need to force a move for mingw32.
267 return std::move(loaded_apk);
268}
269
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800270std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
Winson9947f1e2019-08-16 10:20:39 -0700271 // If this is a resource loader from an .arsc, there will be no zip handle
272 if (zip_handle_ == nullptr) {
273 return {};
274 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700275
Adam Lesinski7ad11102016-10-28 16:39:15 -0700276 ::ZipEntry entry;
Elliott Hughesb97e7372019-05-03 22:42:31 -0700277 int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700278 if (result != 0) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700279 return {};
280 }
281
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700282 const int fd = ::GetFileDescriptor(zip_handle_.get());
283 const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
Adam Lesinski7ad11102016-10-28 16:39:15 -0700284 if (entry.method == kCompressDeflated) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800285 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700286 if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.compressed_length,
287 true /*readOnly*/)) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800288 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
289 return {};
290 }
291
292 std::unique_ptr<Asset> asset =
293 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
294 if (asset == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700295 LOG(ERROR) << "Failed to decompress '" << path << "'.";
296 return {};
297 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800298 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700299 } else {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800300 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700301 if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.uncompressed_length,
302 true /*readOnly*/)) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800303 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700304 return {};
305 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800306
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700307 // TODO: apks created from file descriptors residing in RAM currently cannot open file
308 // descriptors to the assets they contain. This is because the Asset::openFileDeescriptor uses
309 // the zip path on disk to create a new file descriptor. This is fixed in a future change
310 // in the change topic.
311 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
312 unique_fd(-1) /* fd*/, mode);
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800313 if (asset == nullptr) {
314 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
315 return {};
316 }
317 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700318 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800319}
320
321bool ApkAssets::ForEachFile(const std::string& root_path,
322 const std::function<void(const StringPiece&, FileType)>& f) const {
Winson9947f1e2019-08-16 10:20:39 -0700323 // If this is a resource loader from an .arsc, there will be no zip handle
324 if (zip_handle_ == nullptr) {
325 return false;
326 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800327
328 std::string root_path_full = root_path;
329 if (root_path_full.back() != '/') {
330 root_path_full += '/';
331 }
332
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800333 void* cookie;
Elliott Hughes7a6cc0c2019-05-08 12:12:39 -0700334 if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800335 return false;
336 }
337
Elliott Hughes78de4f92019-06-14 15:28:38 -0700338 std::string name;
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800339 ::ZipEntry entry;
340
341 // We need to hold back directories because many paths will contain them and we want to only
342 // surface one.
343 std::set<std::string> dirs;
344
345 int32_t result;
346 while ((result = ::Next(cookie, &entry, &name)) == 0) {
Elliott Hughes78de4f92019-06-14 15:28:38 -0700347 StringPiece full_file_path(name);
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800348 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800349
350 if (!leaf_file_path.empty()) {
351 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
352 if (iter != leaf_file_path.end()) {
353 std::string dir =
354 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
355 dirs.insert(std::move(dir));
356 } else {
357 f(leaf_file_path, kFileTypeRegular);
358 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800359 }
360 }
361 ::EndIteration(cookie);
362
363 // Now present the unique directories.
364 for (const std::string& dir : dirs) {
365 f(dir, kFileTypeDirectory);
366 }
367
368 // -1 is end of iteration, anything else is an error.
369 return result == -1;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700370}
371
Winsonb0085ce2019-02-19 12:48:22 -0800372bool ApkAssets::IsUpToDate() const {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800373 if (IsLoader()) {
374 // Loaders are invalidated by the app, not the system, so assume up to date.
Winson9947f1e2019-08-16 10:20:39 -0700375 return true;
376 }
377
Winsonb0085ce2019-02-19 12:48:22 -0800378 return last_mod_time_ == getFileModDate(path_.c_str());
379}
380
Adam Lesinski7ad11102016-10-28 16:39:15 -0700381} // namespace android