1 /*
2  * Copyright (C) 2021 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 "idmap2/FabricatedOverlay.h"
18 
19 #include <androidfw/ResourceUtils.h>
20 #include <google/protobuf/io/coded_stream.h>
21 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
22 #include <utils/ByteOrder.h>
23 #include <zlib.h>
24 
25 #include <fstream>
26 #include <map>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 
31 namespace android::idmap2 {
32 
33 namespace {
Read32(std::istream & stream,uint32_t * out)34 bool Read32(std::istream& stream, uint32_t* out) {
35   uint32_t value;
36   if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
37     *out = dtohl(value);
38     return true;
39   }
40   return false;
41 }
42 
Write32(std::ostream & stream,uint32_t value)43 void Write32(std::ostream& stream, uint32_t value) {
44   uint32_t x = htodl(value);
45   stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
46 }
47 }  // namespace
48 
FabricatedOverlay(pb::FabricatedOverlay && overlay,std::optional<uint32_t> crc_from_disk)49 FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
50                                      std::optional<uint32_t> crc_from_disk)
51     : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) {
52 }
53 
Builder(const std::string & package_name,const std::string & name,const std::string & target_package_name)54 FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name,
55                                     const std::string& target_package_name) {
56   package_name_ = package_name;
57   name_ = name;
58   target_package_name_ = target_package_name;
59 }
60 
SetOverlayable(const std::string & name)61 FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) {
62   target_overlayable_ = name;
63   return *this;
64 }
65 
SetResourceValue(const std::string & resource_name,uint8_t data_type,uint32_t data_value)66 FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
67     const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
68   entries_.emplace_back(Entry{resource_name, data_type, data_value});
69   return *this;
70 }
71 
Build()72 Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
73   std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries;
74   for (const auto& res_entry : entries_) {
75     StringPiece package_substr;
76     StringPiece type_name;
77     StringPiece entry_name;
78     if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr,
79                                       &type_name, &entry_name)) {
80       return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str());
81     }
82 
83     std::string package_name =
84         package_substr.empty() ? target_package_name_ : package_substr.to_string();
85     if (type_name.empty()) {
86       return Error("resource name '%s' missing type name", res_entry.resource_name.c_str());
87     }
88 
89     if (entry_name.empty()) {
90       return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str());
91     }
92 
93     auto package = entries.find(package_name);
94     if (package == entries.end()) {
95       package = entries
96                     .insert(std::make_pair(
97                         package_name, std::map<std::string, std::map<std::string, TargetValue>>()))
98                     .first;
99     }
100 
101     auto type = package->second.find(type_name.to_string());
102     if (type == package->second.end()) {
103       type =
104           package->second
105               .insert(std::make_pair(type_name.to_string(), std::map<std::string, TargetValue>()))
106               .first;
107     }
108 
109     auto entry = type->second.find(entry_name.to_string());
110     if (entry == type->second.end()) {
111       entry = type->second.insert(std::make_pair(entry_name.to_string(), TargetValue())).first;
112     }
113 
114     entry->second = TargetValue{res_entry.data_type, res_entry.data_value};
115   }
116 
117   pb::FabricatedOverlay overlay_pb;
118   overlay_pb.set_package_name(package_name_);
119   overlay_pb.set_name(name_);
120   overlay_pb.set_target_package_name(target_package_name_);
121   overlay_pb.set_target_overlayable(target_overlayable_);
122 
123   for (const auto& package : entries) {
124     auto package_pb = overlay_pb.add_packages();
125     package_pb->set_name(package.first);
126 
127     for (const auto& type : package.second) {
128       auto type_pb = package_pb->add_types();
129       type_pb->set_name(type.first);
130 
131       for (const auto& entry : type.second) {
132         auto entry_pb = type_pb->add_entries();
133         entry_pb->set_name(entry.first);
134         pb::ResourceValue* value = entry_pb->mutable_res_value();
135         value->set_data_type(entry.second.data_type);
136         value->set_data_value(entry.second.data_value);
137       }
138     }
139   }
140 
141   return FabricatedOverlay(std::move(overlay_pb));
142 }
143 
FromBinaryStream(std::istream & stream)144 Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
145   uint32_t magic;
146   if (!Read32(stream, &magic)) {
147     return Error("Failed to read fabricated overlay magic.");
148   }
149 
150   if (magic != kFabricatedOverlayMagic) {
151     return Error("Not a fabricated overlay file.");
152   }
153 
154   uint32_t version;
155   if (!Read32(stream, &version)) {
156     return Error("Failed to read fabricated overlay version.");
157   }
158 
159   if (version != 1) {
160     return Error("Invalid fabricated overlay version '%u'.", version);
161   }
162 
163   uint32_t crc;
164   if (!Read32(stream, &crc)) {
165     return Error("Failed to read fabricated overlay version.");
166   }
167 
168   pb::FabricatedOverlay overlay{};
169   if (!overlay.ParseFromIstream(&stream)) {
170     return Error("Failed read fabricated overlay proto.");
171   }
172 
173   // If the proto version is the latest version, then the contents of the proto must be the same
174   // when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
175   // proto to the latest version will likely change the contents of the fabricated overlay.
176   return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion
177                                                    ? std::optional<uint32_t>(crc)
178                                                    : std::nullopt);
179 }
180 
InitializeData() const181 Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const {
182   if (!data_.has_value()) {
183     auto size = overlay_pb_.ByteSizeLong();
184     auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
185 
186     // Ensure serialization is deterministic
187     google::protobuf::io::ArrayOutputStream array_stream(data.get(), size);
188     google::protobuf::io::CodedOutputStream output_stream(&array_stream);
189     output_stream.SetSerializationDeterministic(true);
190     overlay_pb_.SerializeWithCachedSizes(&output_stream);
191     if (output_stream.HadError() || size != output_stream.ByteCount()) {
192       return Error("Failed to serialize fabricated overlay.");
193     }
194 
195     // Calculate the crc using the proto data and the version.
196     uint32_t crc = crc32(0L, Z_NULL, 0);
197     crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion),
198                 sizeof(uint32_t));
199     crc = crc32(crc, data.get(), size);
200     data_ = SerializedData{std::move(data), size, crc};
201   }
202   return &(*data_);
203 }
GetCrc() const204 Result<uint32_t> FabricatedOverlay::GetCrc() const {
205   if (crc_from_disk_.has_value()) {
206     return *crc_from_disk_;
207   }
208   auto data = InitializeData();
209   if (!data) {
210     return data.GetError();
211   }
212   return (*data)->crc;
213 }
214 
ToBinaryStream(std::ostream & stream) const215 Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
216   auto data = InitializeData();
217   if (!data) {
218     return data.GetError();
219   }
220 
221   Write32(stream, kFabricatedOverlayMagic);
222   Write32(stream, kFabricatedOverlayCurrentVersion);
223   Write32(stream, (*data)->crc);
224   stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size);
225   if (stream.bad()) {
226     return Error("Failed to write serialized fabricated overlay.");
227   }
228 
229   return Unit{};
230 }
231 
232 using FabContainer = FabricatedOverlayContainer;
FabricatedOverlayContainer(FabricatedOverlay && overlay,std::string && path)233 FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path)
234     : overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) {
235 }
236 
237 FabContainer::~FabricatedOverlayContainer() = default;
238 
FromPath(std::string path)239 Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) {
240   std::fstream fin(path);
241   auto overlay = FabricatedOverlay::FromBinaryStream(fin);
242   if (!overlay) {
243     return overlay.GetError();
244   }
245   return std::unique_ptr<FabContainer>(
246       new FabricatedOverlayContainer(std::move(*overlay), std::move(path)));
247 }
248 
FromOverlay(FabricatedOverlay && overlay)249 std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) {
250   return std::unique_ptr<FabContainer>(
251       new FabricatedOverlayContainer(std::move(overlay), {} /* path */));
252 }
253 
GetManifestInfo() const254 OverlayManifestInfo FabContainer::GetManifestInfo() const {
255   const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
256   return OverlayManifestInfo{
257       .package_name = overlay_pb.package_name(),
258       .name = overlay_pb.name(),
259       .target_package = overlay_pb.target_package_name(),
260       .target_name = overlay_pb.target_overlayable(),
261   };
262 }
263 
FindOverlayInfo(const std::string & name) const264 Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const {
265   const OverlayManifestInfo info = GetManifestInfo();
266   if (name != info.name) {
267     return Error("Failed to find name '%s' in fabricated overlay", name.c_str());
268   }
269   return info;
270 }
271 
GetOverlayData(const OverlayManifestInfo & info) const272 Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const {
273   const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
274   if (info.name != overlay_pb.name()) {
275     return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str());
276   }
277 
278   OverlayData result{};
279   for (const auto& package : overlay_pb.packages()) {
280     for (const auto& type : package.types()) {
281       for (const auto& entry : type.entries()) {
282         auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(),
283                                        entry.name().c_str());
284         const auto& res_value = entry.res_value();
285         result.pairs.emplace_back(OverlayData::Value{
286             name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
287                               .data_value = res_value.data_value()}});
288       }
289     }
290   }
291   return result;
292 }
293 
GetCrc() const294 Result<uint32_t> FabContainer::GetCrc() const {
295   return overlay_.GetCrc();
296 }
297 
GetPath() const298 const std::string& FabContainer::GetPath() const {
299   return path_;
300 }
301 
GetResourceName(ResourceId) const302 Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const {
303   return Error("Fabricated overlay does not contain resources.");
304 }
305 
306 }  // namespace android::idmap2
307