1 /* 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 17 #include "androidfw/ApkAssets.h" 18 19 #include <algorithm> 20 21 #include "android-base/errors.h" 22 #include "android-base/file.h" 23 #include "android-base/logging.h" 24 #include "android-base/unique_fd.h" 25 #include "android-base/utf8.h" 26 #include "utils/Compat.h" 27 #include "utils/FileMap.h" 28 #include "ziparchive/zip_archive.h" 29 30 #include "androidfw/Asset.h" 31 #include "androidfw/Idmap.h" 32 #include "androidfw/misc.h" 33 #include "androidfw/ResourceTypes.h" 34 #include "androidfw/Util.h" 35 36 namespace android { 37 38 using base::SystemErrorCodeToString; 39 using base::unique_fd; 40 41 static const std::string kResourcesArsc("resources.arsc"); 42 43 ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, 44 const std::string& path, 45 time_t last_mod_time) 46 : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) { 47 } 48 49 std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { 50 return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); 51 } 52 53 std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, 54 bool system) { 55 return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); 56 } 57 58 std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, 59 bool system) { 60 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path); 61 if (idmap_asset == nullptr) { 62 return {}; 63 } 64 65 const StringPiece idmap_data( 66 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)), 67 static_cast<size_t>(idmap_asset->getLength())); 68 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data); 69 if (loaded_idmap == nullptr) { 70 LOG(ERROR) << "failed to load IDMAP " << idmap_path; 71 return {}; 72 } 73 return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset), 74 std::move(loaded_idmap), system, false /*load_as_shared_library*/); 75 } 76 77 std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd, 78 const std::string& friendly_name, 79 bool system, bool force_shared_lib) { 80 return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, 81 system, force_shared_lib); 82 } 83 84 std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { 85 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); 86 if (fd == -1) { 87 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); 88 return {}; 89 } 90 91 const off64_t file_len = lseek64(fd, 0, SEEK_END); 92 if (file_len < 0) { 93 LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); 94 return {}; 95 } 96 97 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); 98 if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) { 99 LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); 100 return {}; 101 } 102 return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); 103 } 104 105 std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( 106 unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, 107 std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { 108 ::ZipArchiveHandle unmanaged_handle; 109 int32_t result; 110 if (fd >= 0) { 111 result = 112 ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); 113 } else { 114 result = ::OpenArchive(path.c_str(), &unmanaged_handle); 115 } 116 117 if (result != 0) { 118 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); 119 return {}; 120 } 121 122 time_t last_mod_time = getFileModDate(path.c_str()); 123 124 // Wrap the handle in a unique_ptr so it gets automatically closed. 125 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time)); 126 127 // Find the resource table. 128 ::ZipString entry_name(kResourcesArsc.c_str()); 129 ::ZipEntry entry; 130 result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry); 131 if (result != 0) { 132 // There is no resources.arsc, so create an empty LoadedArsc and return. 133 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); 134 return std::move(loaded_apk); 135 } 136 137 if (entry.method == kCompressDeflated) { 138 LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; 139 } 140 141 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. 142 loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); 143 if (loaded_apk->resources_asset_ == nullptr) { 144 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; 145 return {}; 146 } 147 148 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. 149 loaded_apk->idmap_asset_ = std::move(idmap_asset); 150 151 const StringPiece data( 152 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), 153 loaded_apk->resources_asset_->getLength()); 154 loaded_apk->loaded_arsc_ = 155 LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); 156 if (loaded_apk->loaded_arsc_ == nullptr) { 157 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; 158 return {}; 159 } 160 161 // Need to force a move for mingw32. 162 return std::move(loaded_apk); 163 } 164 165 std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { 166 CHECK(zip_handle_ != nullptr); 167 168 ::ZipString name(path.c_str()); 169 ::ZipEntry entry; 170 int32_t result = ::FindEntry(zip_handle_.get(), name, &entry); 171 if (result != 0) { 172 return {}; 173 } 174 175 if (entry.method == kCompressDeflated) { 176 std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); 177 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, 178 entry.compressed_length, true /*readOnly*/)) { 179 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; 180 return {}; 181 } 182 183 std::unique_ptr<Asset> asset = 184 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); 185 if (asset == nullptr) { 186 LOG(ERROR) << "Failed to decompress '" << path << "'."; 187 return {}; 188 } 189 return asset; 190 } else { 191 std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); 192 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, 193 entry.uncompressed_length, true /*readOnly*/)) { 194 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; 195 return {}; 196 } 197 198 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode); 199 if (asset == nullptr) { 200 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; 201 return {}; 202 } 203 return asset; 204 } 205 } 206 207 bool ApkAssets::ForEachFile(const std::string& root_path, 208 const std::function<void(const StringPiece&, FileType)>& f) const { 209 CHECK(zip_handle_ != nullptr); 210 211 std::string root_path_full = root_path; 212 if (root_path_full.back() != '/') { 213 root_path_full += '/'; 214 } 215 216 ::ZipString prefix(root_path_full.c_str()); 217 void* cookie; 218 if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) { 219 return false; 220 } 221 222 ::ZipString name; 223 ::ZipEntry entry; 224 225 // We need to hold back directories because many paths will contain them and we want to only 226 // surface one. 227 std::set<std::string> dirs; 228 229 int32_t result; 230 while ((result = ::Next(cookie, &entry, &name)) == 0) { 231 StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length); 232 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); 233 234 if (!leaf_file_path.empty()) { 235 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); 236 if (iter != leaf_file_path.end()) { 237 std::string dir = 238 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); 239 dirs.insert(std::move(dir)); 240 } else { 241 f(leaf_file_path, kFileTypeRegular); 242 } 243 } 244 } 245 ::EndIteration(cookie); 246 247 // Now present the unique directories. 248 for (const std::string& dir : dirs) { 249 f(dir, kFileTypeDirectory); 250 } 251 252 // -1 is end of iteration, anything else is an error. 253 return result == -1; 254 } 255 256 bool ApkAssets::IsUpToDate() const { 257 return last_mod_time_ == getFileModDate(path_.c_str()); 258 } 259 260 } // namespace android 261