1 //
2 // Copyright (C) 2015 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 "update_engine/payload_generator/payload_file.h"
18 
19 #include <endian.h>
20 
21 #include <algorithm>
22 
23 #include "update_engine/common/hash_calculator.h"
24 #include "update_engine/payload_consumer/delta_performer.h"
25 #include "update_engine/payload_consumer/file_writer.h"
26 #include "update_engine/payload_consumer/payload_constants.h"
27 #include "update_engine/payload_generator/annotated_operation.h"
28 #include "update_engine/payload_generator/delta_diff_utils.h"
29 #include "update_engine/payload_generator/payload_signer.h"
30 
31 using std::string;
32 using std::vector;
33 
34 namespace chromeos_update_engine {
35 
36 namespace {
37 
38 struct DeltaObject {
DeltaObjectchromeos_update_engine::__anon3fa7f6d60111::DeltaObject39   DeltaObject(const string& in_name, const int in_type, const off_t in_size)
40       : name(in_name),
41         type(in_type),
42         size(in_size) {}
operator <chromeos_update_engine::__anon3fa7f6d60111::DeltaObject43   bool operator <(const DeltaObject& object) const {
44     return (size != object.size) ? (size < object.size) : (name < object.name);
45   }
46   string name;
47   int type;
48   off_t size;
49 };
50 
51 // Writes the uint64_t passed in in host-endian to the file as big-endian.
52 // Returns true on success.
WriteUint64AsBigEndian(FileWriter * writer,const uint64_t value)53 bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
54   uint64_t value_be = htobe64(value);
55   TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
56   return true;
57 }
58 
59 }  // namespace
60 
Init(const PayloadGenerationConfig & config)61 bool PayloadFile::Init(const PayloadGenerationConfig& config) {
62   TEST_AND_RETURN_FALSE(config.version.Validate());
63   major_version_ = config.version.major;
64   manifest_.set_minor_version(config.version.minor);
65 
66   if (!config.source.ImageInfoIsEmpty())
67     *(manifest_.mutable_old_image_info()) = config.source.image_info;
68 
69   if (!config.target.ImageInfoIsEmpty())
70     *(manifest_.mutable_new_image_info()) = config.target.image_info;
71 
72   manifest_.set_block_size(config.block_size);
73   return true;
74 }
75 
AddPartition(const PartitionConfig & old_conf,const PartitionConfig & new_conf,const vector<AnnotatedOperation> & aops)76 bool PayloadFile::AddPartition(const PartitionConfig& old_conf,
77                                const PartitionConfig& new_conf,
78                                const vector<AnnotatedOperation>& aops) {
79   // Check partitions order for Chrome OS
80   if (major_version_ == kChromeOSMajorPayloadVersion) {
81     const vector<const char*> part_order = { kLegacyPartitionNameRoot,
82                                              kLegacyPartitionNameKernel };
83     TEST_AND_RETURN_FALSE(part_vec_.size() < part_order.size());
84     TEST_AND_RETURN_FALSE(new_conf.name == part_order[part_vec_.size()]);
85   }
86   Partition part;
87   part.name = new_conf.name;
88   part.aops = aops;
89   part.postinstall = new_conf.postinstall;
90   // Initialize the PartitionInfo objects if present.
91   if (!old_conf.path.empty())
92     TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(old_conf,
93                                                               &part.old_info));
94   TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(new_conf,
95                                                             &part.new_info));
96   part_vec_.push_back(std::move(part));
97   return true;
98 }
99 
WritePayload(const string & payload_file,const string & data_blobs_path,const string & private_key_path,uint64_t * metadata_size_out)100 bool PayloadFile::WritePayload(const string& payload_file,
101                                const string& data_blobs_path,
102                                const string& private_key_path,
103                                uint64_t* metadata_size_out) {
104   // Reorder the data blobs with the manifest_.
105   string ordered_blobs_path;
106   TEST_AND_RETURN_FALSE(utils::MakeTempFile(
107       "CrAU_temp_data.ordered.XXXXXX",
108       &ordered_blobs_path,
109       nullptr));
110   ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
111   TEST_AND_RETURN_FALSE(ReorderDataBlobs(data_blobs_path, ordered_blobs_path));
112 
113   // Check that install op blobs are in order.
114   uint64_t next_blob_offset = 0;
115   for (const auto& part : part_vec_) {
116     for (const auto& aop : part.aops) {
117       if (!aop.op.has_data_offset())
118         continue;
119       if (aop.op.data_offset() != next_blob_offset) {
120         LOG(FATAL) << "bad blob offset! " << aop.op.data_offset() << " != "
121                    << next_blob_offset;
122       }
123       next_blob_offset += aop.op.data_length();
124     }
125   }
126 
127   // Copy the operations and partition info from the part_vec_ to the manifest.
128   manifest_.clear_install_operations();
129   manifest_.clear_kernel_install_operations();
130   manifest_.clear_partitions();
131   for (const auto& part : part_vec_) {
132     if (major_version_ == kBrilloMajorPayloadVersion) {
133       PartitionUpdate* partition = manifest_.add_partitions();
134       partition->set_partition_name(part.name);
135       if (part.postinstall.run) {
136         partition->set_run_postinstall(true);
137         if (!part.postinstall.path.empty())
138           partition->set_postinstall_path(part.postinstall.path);
139         if (!part.postinstall.filesystem_type.empty())
140           partition->set_filesystem_type(part.postinstall.filesystem_type);
141       }
142       for (const AnnotatedOperation& aop : part.aops) {
143         *partition->add_operations() = aop.op;
144       }
145       if (part.old_info.has_size() || part.old_info.has_hash())
146         *(partition->mutable_old_partition_info()) = part.old_info;
147       if (part.new_info.has_size() || part.new_info.has_hash())
148         *(partition->mutable_new_partition_info()) = part.new_info;
149     } else {
150       // major_version_ == kChromeOSMajorPayloadVersion
151       if (part.name == kLegacyPartitionNameKernel) {
152         for (const AnnotatedOperation& aop : part.aops)
153           *manifest_.add_kernel_install_operations() = aop.op;
154         if (part.old_info.has_size() || part.old_info.has_hash())
155           *manifest_.mutable_old_kernel_info() = part.old_info;
156         if (part.new_info.has_size() || part.new_info.has_hash())
157           *manifest_.mutable_new_kernel_info() = part.new_info;
158       } else {
159         for (const AnnotatedOperation& aop : part.aops)
160           *manifest_.add_install_operations() = aop.op;
161         if (part.old_info.has_size() || part.old_info.has_hash())
162           *manifest_.mutable_old_rootfs_info() = part.old_info;
163         if (part.new_info.has_size() || part.new_info.has_hash())
164           *manifest_.mutable_new_rootfs_info() = part.new_info;
165       }
166     }
167   }
168 
169   // Signatures appear at the end of the blobs. Note the offset in the
170   // manifest_.
171   uint64_t signature_blob_length = 0;
172   if (!private_key_path.empty()) {
173     TEST_AND_RETURN_FALSE(
174         PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
175                                            &signature_blob_length));
176     PayloadSigner::AddSignatureToManifest(
177         next_blob_offset, signature_blob_length,
178         major_version_ == kChromeOSMajorPayloadVersion, &manifest_);
179   }
180 
181   // Serialize protobuf
182   string serialized_manifest;
183   TEST_AND_RETURN_FALSE(manifest_.AppendToString(&serialized_manifest));
184 
185   uint64_t metadata_size =
186       sizeof(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
187 
188   LOG(INFO) << "Writing final delta file header...";
189   DirectFileWriter writer;
190   TEST_AND_RETURN_FALSE_ERRNO(writer.Open(payload_file.c_str(),
191                                           O_WRONLY | O_CREAT | O_TRUNC,
192                                           0644) == 0);
193   ScopedFileWriterCloser writer_closer(&writer);
194 
195   // Write header
196   TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, sizeof(kDeltaMagic)));
197 
198   // Write major version number
199   TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, major_version_));
200 
201   // Write protobuf length
202   TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
203                                                serialized_manifest.size()));
204 
205   // Write metadata signature size.
206   uint32_t metadata_signature_size = 0;
207   if (major_version_ == kBrilloMajorPayloadVersion) {
208     // Metadata signature has the same size as payload signature, because they
209     // are both the same kind of signature for the same kind of hash.
210     uint32_t metadata_signature_size = htobe32(signature_blob_length);
211     TEST_AND_RETURN_FALSE(writer.Write(&metadata_signature_size,
212                                        sizeof(metadata_signature_size)));
213     metadata_size += sizeof(metadata_signature_size);
214     // Set correct size instead of big endian size.
215     metadata_signature_size = signature_blob_length;
216   }
217 
218   // Write protobuf
219   LOG(INFO) << "Writing final delta file protobuf... "
220             << serialized_manifest.size();
221   TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
222                                      serialized_manifest.size()));
223 
224   // Write metadata signature blob.
225   if (major_version_ == kBrilloMajorPayloadVersion &&
226       !private_key_path.empty()) {
227     brillo::Blob metadata_hash, metadata_signature;
228     TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(payload_file,
229                                                              metadata_size,
230                                                              &metadata_hash));
231     TEST_AND_RETURN_FALSE(
232         PayloadSigner::SignHashWithKeys(metadata_hash,
233                                         vector<string>(1, private_key_path),
234                                         &metadata_signature));
235     TEST_AND_RETURN_FALSE(writer.Write(metadata_signature.data(),
236                                        metadata_signature.size()));
237   }
238 
239   // Append the data blobs
240   LOG(INFO) << "Writing final delta file data blobs...";
241   int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
242   ScopedFdCloser blobs_fd_closer(&blobs_fd);
243   TEST_AND_RETURN_FALSE(blobs_fd >= 0);
244   for (;;) {
245     vector<char> buf(1024 * 1024);
246     ssize_t rc = read(blobs_fd, buf.data(), buf.size());
247     if (0 == rc) {
248       // EOF
249       break;
250     }
251     TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
252     TEST_AND_RETURN_FALSE(writer.Write(buf.data(), rc));
253   }
254 
255   // Write payload signature blob.
256   if (!private_key_path.empty()) {
257     LOG(INFO) << "Signing the update...";
258     brillo::Blob signature_blob;
259     TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
260         payload_file,
261         vector<string>(1, private_key_path),
262         metadata_size,
263         metadata_signature_size,
264         metadata_size + metadata_signature_size + manifest_.signatures_offset(),
265         &signature_blob));
266     TEST_AND_RETURN_FALSE(writer.Write(signature_blob.data(),
267                                        signature_blob.size()));
268   }
269 
270   ReportPayloadUsage(metadata_size);
271   *metadata_size_out = metadata_size;
272   return true;
273 }
274 
ReorderDataBlobs(const string & data_blobs_path,const string & new_data_blobs_path)275 bool PayloadFile::ReorderDataBlobs(
276     const string& data_blobs_path,
277     const string& new_data_blobs_path) {
278   int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
279   TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
280   ScopedFdCloser in_fd_closer(&in_fd);
281 
282   DirectFileWriter writer;
283   TEST_AND_RETURN_FALSE(
284       writer.Open(new_data_blobs_path.c_str(),
285                   O_WRONLY | O_TRUNC | O_CREAT,
286                   0644) == 0);
287   ScopedFileWriterCloser writer_closer(&writer);
288   uint64_t out_file_size = 0;
289 
290   for (auto& part : part_vec_) {
291     for (AnnotatedOperation& aop : part.aops) {
292       if (!aop.op.has_data_offset())
293         continue;
294       CHECK(aop.op.has_data_length());
295       brillo::Blob buf(aop.op.data_length());
296       ssize_t rc = pread(in_fd, buf.data(), buf.size(), aop.op.data_offset());
297       TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
298 
299       // Add the hash of the data blobs for this operation
300       TEST_AND_RETURN_FALSE(AddOperationHash(&aop.op, buf));
301 
302       aop.op.set_data_offset(out_file_size);
303       TEST_AND_RETURN_FALSE(writer.Write(buf.data(), buf.size()));
304       out_file_size += buf.size();
305     }
306   }
307   return true;
308 }
309 
AddOperationHash(InstallOperation * op,const brillo::Blob & buf)310 bool PayloadFile::AddOperationHash(InstallOperation* op,
311                                    const brillo::Blob& buf) {
312   HashCalculator hasher;
313   TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
314   TEST_AND_RETURN_FALSE(hasher.Finalize());
315   const brillo::Blob& hash = hasher.raw_hash();
316   op->set_data_sha256_hash(hash.data(), hash.size());
317   return true;
318 }
319 
ReportPayloadUsage(uint64_t metadata_size) const320 void PayloadFile::ReportPayloadUsage(uint64_t metadata_size) const {
321   vector<DeltaObject> objects;
322   off_t total_size = 0;
323 
324   for (const auto& part : part_vec_) {
325     for (const AnnotatedOperation& aop : part.aops) {
326       objects.push_back(DeltaObject(aop.name,
327                                     aop.op.type(),
328                                     aop.op.data_length()));
329       total_size += aop.op.data_length();
330     }
331   }
332 
333   objects.push_back(DeltaObject("<manifest-metadata>",
334                                 -1,
335                                 metadata_size));
336   total_size += metadata_size;
337 
338   std::sort(objects.begin(), objects.end());
339 
340   static const char kFormatString[] = "%6.2f%% %10jd %-10s %s\n";
341   for (const DeltaObject& object : objects) {
342     fprintf(
343         stderr, kFormatString,
344         object.size * 100.0 / total_size,
345         static_cast<intmax_t>(object.size),
346         (object.type >= 0 ? InstallOperationTypeName(
347                                 static_cast<InstallOperation_Type>(object.type))
348                           : "-"),
349         object.name.c_str());
350   }
351   fprintf(stderr, kFormatString,
352           100.0, static_cast<intmax_t>(total_size), "", "<total>");
353 }
354 
355 }  // namespace chromeos_update_engine
356