blob: b2b0ec2a54f8e5db5b5eb580cf0ca7d7e4dc115d [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,
44 const 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)
Winson9947f1e2019-08-16 10:20:39 -070047 : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
Ryan Mitchell73bfe412019-11-12 16:22:04 -080048 property_flags_(property_flags) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -070049}
Adam Lesinski03ebac82017-09-25 13:10:14 -070050
Winson9947f1e2019-08-16 10:20:39 -070051std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
52 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -080053 package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
54 (for_loader ? PROPERTY_LOADER : 0U);
55 return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
Adam Lesinskida431a22016-12-29 16:08:16 -050056}
57
Adam Lesinski0c405242017-01-13 20:47:26 -080058std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
59 bool system) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -080060 package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U);
61 return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
Adam Lesinskida431a22016-12-29 16:08:16 -050062}
63
Adam Lesinski970bd8d2017-09-25 13:21:55 -070064std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
65 bool system) {
66 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
67 if (idmap_asset == nullptr) {
68 return {};
69 }
70
71 const StringPiece idmap_data(
72 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
73 static_cast<size_t>(idmap_asset->getLength()));
74 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data);
75 if (loaded_idmap == nullptr) {
76 LOG(ERROR) << "failed to load IDMAP " << idmap_path;
77 return {};
78 }
Ryan Mitchell73bfe412019-11-12 16:22:04 -080079
Ryan Mitchelle35e87f2020-03-19 16:57:50 -070080 auto apkPath = loaded_idmap->OverlayApkPath();
81 return LoadImpl({} /*fd*/, apkPath,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080082 std::move(idmap_asset),
83 std::move(loaded_idmap),
84 PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
Adam Lesinski441500b2017-11-13 17:52:25 -080085}
86
87std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
88 const std::string& friendly_name,
Winson9947f1e2019-08-16 10:20:39 -070089 bool system, bool force_shared_lib,
90 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -080091 package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
92 (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
93 (for_loader ? PROPERTY_LOADER : 0U);
Adam Lesinski441500b2017-11-13 17:52:25 -080094 return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080095 flags);
Winson9947f1e2019-08-16 10:20:39 -070096}
97
98std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
99 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800100 return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
Winson9947f1e2019-08-16 10:20:39 -0700101}
102
103std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
104 const std::string& friendly_name,
105 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800106 return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700107}
108
109std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
110 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
111 if (fd == -1) {
112 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
113 return {};
114 }
115
116 const off64_t file_len = lseek64(fd, 0, SEEK_END);
117 if (file_len < 0) {
118 LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno);
119 return {};
120 }
121
122 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
123 if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) {
124 LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno);
125 return {};
126 }
127 return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
128}
129
130std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
Adam Lesinski441500b2017-11-13 17:52:25 -0800131 unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800132 std::unique_ptr<const LoadedIdmap> loaded_idmap, package_property_t property_flags) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700133 ::ZipArchiveHandle unmanaged_handle;
Adam Lesinski441500b2017-11-13 17:52:25 -0800134 int32_t result;
135 if (fd >= 0) {
136 result =
137 ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/);
138 } else {
139 result = ::OpenArchive(path.c_str(), &unmanaged_handle);
140 }
141
Adam Lesinski7ad11102016-10-28 16:39:15 -0700142 if (result != 0) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700143 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
Songchun Fan898b3162019-07-08 09:00:34 -0700144 ::CloseArchive(unmanaged_handle);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700145 return {};
146 }
147
Winsonb0085ce2019-02-19 12:48:22 -0800148 time_t last_mod_time = getFileModDate(path.c_str());
149
Adam Lesinski7ad11102016-10-28 16:39:15 -0700150 // Wrap the handle in a unique_ptr so it gets automatically closed.
Winson9947f1e2019-08-16 10:20:39 -0700151 std::unique_ptr<ApkAssets>
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800152 loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700153
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700154 // Find the resource table.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700155 ::ZipEntry entry;
Elliott Hughesb97e7372019-05-03 22:42:31 -0700156 result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700157 if (result != 0) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700158 // There is no resources.arsc, so create an empty LoadedArsc and return.
159 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
160 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700161 }
162
163 if (entry.method == kCompressDeflated) {
Ryan Mitchell31b11052019-06-13 13:47:26 -0700164 ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700165 }
166
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700167 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
168 loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700169 if (loaded_apk->resources_asset_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700170 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700171 return {};
172 }
173
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700174 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
175 loaded_apk->idmap_asset_ = std::move(idmap_asset);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700176 loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700177
178 const StringPiece data(
179 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
180 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800181 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
182 property_flags);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700183 if (loaded_apk->loaded_arsc_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700184 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700185 return {};
186 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800187
188 // Need to force a move for mingw32.
189 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700190}
191
Winson9947f1e2019-08-16 10:20:39 -0700192std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
193 const std::string& path,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800194 package_property_t property_flags) {
Winson9947f1e2019-08-16 10:20:39 -0700195 std::unique_ptr<Asset> resources_asset;
196
197 if (fd >= 0) {
198 resources_asset = std::unique_ptr<Asset>(Asset::createFromFd(fd.release(), nullptr,
199 Asset::AccessMode::ACCESS_BUFFER));
200 } else {
201 resources_asset = CreateAssetFromFile(path);
202 }
203
204 if (resources_asset == nullptr) {
205 LOG(ERROR) << "Failed to open ARSC '" << path;
206 return {};
207 }
208
209 time_t last_mod_time = getFileModDate(path.c_str());
210
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800211 std::unique_ptr<ApkAssets> loaded_apk(
212 new ApkAssets(nullptr, path, last_mod_time, property_flags));
Winson9947f1e2019-08-16 10:20:39 -0700213 loaded_apk->resources_asset_ = std::move(resources_asset);
214
215 const StringPiece data(
216 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
217 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800218 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
Winson9947f1e2019-08-16 10:20:39 -0700219 if (loaded_apk->loaded_arsc_ == nullptr) {
220 LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
221 return {};
222 }
223
224 // Need to force a move for mingw32.
225 return std::move(loaded_apk);
226}
227
228std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(bool for_loader) {
229 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, "", -1, for_loader));
230 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
231 // Need to force a move for mingw32.
232 return std::move(loaded_apk);
233}
234
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800235std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
Winson9947f1e2019-08-16 10:20:39 -0700236 // If this is a resource loader from an .arsc, there will be no zip handle
237 if (zip_handle_ == nullptr) {
238 return {};
239 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700240
Adam Lesinski7ad11102016-10-28 16:39:15 -0700241 ::ZipEntry entry;
Elliott Hughesb97e7372019-05-03 22:42:31 -0700242 int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700243 if (result != 0) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700244 return {};
245 }
246
247 if (entry.method == kCompressDeflated) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800248 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
249 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
250 entry.compressed_length, true /*readOnly*/)) {
251 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
252 return {};
253 }
254
255 std::unique_ptr<Asset> asset =
256 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
257 if (asset == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700258 LOG(ERROR) << "Failed to decompress '" << path << "'.";
259 return {};
260 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800261 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700262 } else {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800263 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
264 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
265 entry.uncompressed_length, true /*readOnly*/)) {
266 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700267 return {};
268 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800269
270 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
271 if (asset == nullptr) {
272 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
273 return {};
274 }
275 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700276 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800277}
278
279bool ApkAssets::ForEachFile(const std::string& root_path,
280 const std::function<void(const StringPiece&, FileType)>& f) const {
Winson9947f1e2019-08-16 10:20:39 -0700281 // If this is a resource loader from an .arsc, there will be no zip handle
282 if (zip_handle_ == nullptr) {
283 return false;
284 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800285
286 std::string root_path_full = root_path;
287 if (root_path_full.back() != '/') {
288 root_path_full += '/';
289 }
290
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800291 void* cookie;
Elliott Hughes7a6cc0c2019-05-08 12:12:39 -0700292 if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800293 return false;
294 }
295
Elliott Hughes78de4f92019-06-14 15:28:38 -0700296 std::string name;
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800297 ::ZipEntry entry;
298
299 // We need to hold back directories because many paths will contain them and we want to only
300 // surface one.
301 std::set<std::string> dirs;
302
303 int32_t result;
304 while ((result = ::Next(cookie, &entry, &name)) == 0) {
Elliott Hughes78de4f92019-06-14 15:28:38 -0700305 StringPiece full_file_path(name);
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800306 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800307
308 if (!leaf_file_path.empty()) {
309 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
310 if (iter != leaf_file_path.end()) {
311 std::string dir =
312 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
313 dirs.insert(std::move(dir));
314 } else {
315 f(leaf_file_path, kFileTypeRegular);
316 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800317 }
318 }
319 ::EndIteration(cookie);
320
321 // Now present the unique directories.
322 for (const std::string& dir : dirs) {
323 f(dir, kFileTypeDirectory);
324 }
325
326 // -1 is end of iteration, anything else is an error.
327 return result == -1;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700328}
329
Winsonb0085ce2019-02-19 12:48:22 -0800330bool ApkAssets::IsUpToDate() const {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800331 if (IsLoader()) {
332 // Loaders are invalidated by the app, not the system, so assume up to date.
Winson9947f1e2019-08-16 10:20:39 -0700333 return true;
334 }
335
Winsonb0085ce2019-02-19 12:48:22 -0800336 return last_mod_time_ == getFileModDate(path_.c_str());
337}
338
Adam Lesinski7ad11102016-10-28 16:39:15 -0700339} // namespace android