blob: 18934fd55bade583981a60c877305cb1b7e8a9c9 [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
80 return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(),
81 std::move(idmap_asset),
82 std::move(loaded_idmap),
83 PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
Adam Lesinski441500b2017-11-13 17:52:25 -080084}
85
86std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
87 const std::string& friendly_name,
Winson9947f1e2019-08-16 10:20:39 -070088 bool system, bool force_shared_lib,
89 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -080090 package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
91 (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
92 (for_loader ? PROPERTY_LOADER : 0U);
Adam Lesinski441500b2017-11-13 17:52:25 -080093 return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080094 flags);
Winson9947f1e2019-08-16 10:20:39 -070095}
96
97std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
98 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -080099 return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
Winson9947f1e2019-08-16 10:20:39 -0700100}
101
102std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
103 const std::string& friendly_name,
104 bool for_loader) {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800105 return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700106}
107
108std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
109 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
110 if (fd == -1) {
111 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
112 return {};
113 }
114
115 const off64_t file_len = lseek64(fd, 0, SEEK_END);
116 if (file_len < 0) {
117 LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno);
118 return {};
119 }
120
121 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
122 if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) {
123 LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno);
124 return {};
125 }
126 return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
127}
128
129std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
Adam Lesinski441500b2017-11-13 17:52:25 -0800130 unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800131 std::unique_ptr<const LoadedIdmap> loaded_idmap, package_property_t property_flags) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700132 ::ZipArchiveHandle unmanaged_handle;
Adam Lesinski441500b2017-11-13 17:52:25 -0800133 int32_t result;
134 if (fd >= 0) {
135 result =
136 ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/);
137 } else {
138 result = ::OpenArchive(path.c_str(), &unmanaged_handle);
139 }
140
Adam Lesinski7ad11102016-10-28 16:39:15 -0700141 if (result != 0) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700142 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
Songchun Fan898b3162019-07-08 09:00:34 -0700143 ::CloseArchive(unmanaged_handle);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700144 return {};
145 }
146
Winsonb0085ce2019-02-19 12:48:22 -0800147 time_t last_mod_time = getFileModDate(path.c_str());
148
Adam Lesinski7ad11102016-10-28 16:39:15 -0700149 // Wrap the handle in a unique_ptr so it gets automatically closed.
Winson9947f1e2019-08-16 10:20:39 -0700150 std::unique_ptr<ApkAssets>
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800151 loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700152
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700153 // Find the resource table.
Adam Lesinski7ad11102016-10-28 16:39:15 -0700154 ::ZipEntry entry;
Elliott Hughesb97e7372019-05-03 22:42:31 -0700155 result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700156 if (result != 0) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700157 // There is no resources.arsc, so create an empty LoadedArsc and return.
158 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
159 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700160 }
161
162 if (entry.method == kCompressDeflated) {
Ryan Mitchell31b11052019-06-13 13:47:26 -0700163 ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700164 }
165
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700166 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
167 loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700168 if (loaded_apk->resources_asset_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700169 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700170 return {};
171 }
172
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700173 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
174 loaded_apk->idmap_asset_ = std::move(idmap_asset);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700175 loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700176
177 const StringPiece data(
178 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
179 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800180 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
181 property_flags);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700182 if (loaded_apk->loaded_arsc_ == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700183 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700184 return {};
185 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800186
187 // Need to force a move for mingw32.
188 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700189}
190
Winson9947f1e2019-08-16 10:20:39 -0700191std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
192 const std::string& path,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800193 package_property_t property_flags) {
Winson9947f1e2019-08-16 10:20:39 -0700194 std::unique_ptr<Asset> resources_asset;
195
196 if (fd >= 0) {
197 resources_asset = std::unique_ptr<Asset>(Asset::createFromFd(fd.release(), nullptr,
198 Asset::AccessMode::ACCESS_BUFFER));
199 } else {
200 resources_asset = CreateAssetFromFile(path);
201 }
202
203 if (resources_asset == nullptr) {
204 LOG(ERROR) << "Failed to open ARSC '" << path;
205 return {};
206 }
207
208 time_t last_mod_time = getFileModDate(path.c_str());
209
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800210 std::unique_ptr<ApkAssets> loaded_apk(
211 new ApkAssets(nullptr, path, last_mod_time, property_flags));
Winson9947f1e2019-08-16 10:20:39 -0700212 loaded_apk->resources_asset_ = std::move(resources_asset);
213
214 const StringPiece data(
215 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
216 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800217 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
Winson9947f1e2019-08-16 10:20:39 -0700218 if (loaded_apk->loaded_arsc_ == nullptr) {
219 LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
220 return {};
221 }
222
223 // Need to force a move for mingw32.
224 return std::move(loaded_apk);
225}
226
227std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(bool for_loader) {
228 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, "", -1, for_loader));
229 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
230 // Need to force a move for mingw32.
231 return std::move(loaded_apk);
232}
233
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800234std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
Winson9947f1e2019-08-16 10:20:39 -0700235 // If this is a resource loader from an .arsc, there will be no zip handle
236 if (zip_handle_ == nullptr) {
237 return {};
238 }
Adam Lesinski7ad11102016-10-28 16:39:15 -0700239
Adam Lesinski7ad11102016-10-28 16:39:15 -0700240 ::ZipEntry entry;
Elliott Hughesb97e7372019-05-03 22:42:31 -0700241 int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700242 if (result != 0) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700243 return {};
244 }
245
246 if (entry.method == kCompressDeflated) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800247 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
248 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
249 entry.compressed_length, true /*readOnly*/)) {
250 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
251 return {};
252 }
253
254 std::unique_ptr<Asset> asset =
255 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
256 if (asset == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700257 LOG(ERROR) << "Failed to decompress '" << path << "'.";
258 return {};
259 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800260 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700261 } else {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800262 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
263 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
264 entry.uncompressed_length, true /*readOnly*/)) {
265 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700266 return {};
267 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800268
269 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
270 if (asset == nullptr) {
271 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
272 return {};
273 }
274 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700275 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800276}
277
278bool ApkAssets::ForEachFile(const std::string& root_path,
279 const std::function<void(const StringPiece&, FileType)>& f) const {
Winson9947f1e2019-08-16 10:20:39 -0700280 // If this is a resource loader from an .arsc, there will be no zip handle
281 if (zip_handle_ == nullptr) {
282 return false;
283 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800284
285 std::string root_path_full = root_path;
286 if (root_path_full.back() != '/') {
287 root_path_full += '/';
288 }
289
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800290 void* cookie;
Elliott Hughes7a6cc0c2019-05-08 12:12:39 -0700291 if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800292 return false;
293 }
294
Elliott Hughes78de4f92019-06-14 15:28:38 -0700295 std::string name;
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800296 ::ZipEntry entry;
297
298 // We need to hold back directories because many paths will contain them and we want to only
299 // surface one.
300 std::set<std::string> dirs;
301
302 int32_t result;
303 while ((result = ::Next(cookie, &entry, &name)) == 0) {
Elliott Hughes78de4f92019-06-14 15:28:38 -0700304 StringPiece full_file_path(name);
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800305 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800306
307 if (!leaf_file_path.empty()) {
308 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
309 if (iter != leaf_file_path.end()) {
310 std::string dir =
311 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
312 dirs.insert(std::move(dir));
313 } else {
314 f(leaf_file_path, kFileTypeRegular);
315 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800316 }
317 }
318 ::EndIteration(cookie);
319
320 // Now present the unique directories.
321 for (const std::string& dir : dirs) {
322 f(dir, kFileTypeDirectory);
323 }
324
325 // -1 is end of iteration, anything else is an error.
326 return result == -1;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700327}
328
Winsonb0085ce2019-02-19 12:48:22 -0800329bool ApkAssets::IsUpToDate() const {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800330 if (IsLoader()) {
331 // Loaders are invalidated by the app, not the system, so assume up to date.
Winson9947f1e2019-08-16 10:20:39 -0700332 return true;
333 }
334
Winsonb0085ce2019-02-19 12:48:22 -0800335 return last_mod_time_ == getFileModDate(path_.c_str());
336}
337
Adam Lesinski7ad11102016-10-28 16:39:15 -0700338} // namespace android