blob: d3cc0baf2c0e3a14fd733cd271c664e3a3c7d2a7 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
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//
Alex Deymo14158572015-06-13 03:37:08 -070016
17#include "update_engine/payload_generator/payload_file.h"
18
19#include <algorithm>
20
21#include "update_engine/file_writer.h"
22#include "update_engine/omaha_hash_calculator.h"
23#include "update_engine/payload_constants.h"
24#include "update_engine/payload_generator/annotated_operation.h"
25#include "update_engine/payload_generator/delta_diff_generator.h"
26#include "update_engine/payload_generator/delta_diff_utils.h"
27#include "update_engine/payload_generator/payload_signer.h"
28
29using std::string;
30using std::vector;
31
32namespace chromeos_update_engine {
33
34namespace {
35
36const uint64_t kMajorVersionNumber = 1;
37
38static const char* kInstallOperationTypes[] = {
39 "REPLACE",
40 "REPLACE_BZ",
41 "MOVE",
42 "BSDIFF",
43 "SOURCE_COPY",
44 "SOURCE_BSDIFF"
45};
46
47struct DeltaObject {
48 DeltaObject(const string& in_name, const int in_type, const off_t in_size)
49 : name(in_name),
50 type(in_type),
51 size(in_size) {}
52 bool operator <(const DeltaObject& object) const {
53 return (size != object.size) ? (size < object.size) : (name < object.name);
54 }
55 string name;
56 int type;
57 off_t size;
58};
59
60// Writes the uint64_t passed in in host-endian to the file as big-endian.
61// Returns true on success.
62bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
63 uint64_t value_be = htobe64(value);
64 TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
65 return true;
66}
67
68} // namespace
69
70const vector<PartitionName> PayloadFile::partition_disk_order_ = {
71 PartitionName::kRootfs,
72 PartitionName::kKernel,
73};
74
75bool PayloadFile::Init(const PayloadGenerationConfig& config) {
76 manifest_.set_minor_version(config.minor_version);
77
78 if (!config.source.ImageInfoIsEmpty())
79 *(manifest_.mutable_old_image_info()) = config.source.image_info;
80
81 if (!config.target.ImageInfoIsEmpty())
82 *(manifest_.mutable_new_image_info()) = config.target.image_info;
83
84 manifest_.set_block_size(config.block_size);
85
86 // Initialize the PartitionInfo objects if present.
87 if (!config.source.kernel.path.empty()) {
88 TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(
89 config.source.kernel,
90 manifest_.mutable_old_kernel_info()));
91 }
92 TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(
93 config.target.kernel,
94 manifest_.mutable_new_kernel_info()));
95 if (!config.source.rootfs.path.empty()) {
96 TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(
97 config.source.rootfs,
98 manifest_.mutable_old_rootfs_info()));
99 }
100 TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(
101 config.target.rootfs,
102 manifest_.mutable_new_rootfs_info()));
103 return true;
104}
105
106void PayloadFile::AddPartitionOperations(
107 PartitionName name,
108 const vector<AnnotatedOperation>& aops) {
109 aops_map_[name].insert(aops_map_[name].end(), aops.begin(), aops.end());
110}
111
112bool PayloadFile::WritePayload(const string& payload_file,
113 const string& data_blobs_path,
114 const string& private_key_path,
115 uint64_t* medatata_size_out) {
116 // Reorder the data blobs with the manifest_.
117 string ordered_blobs_path;
118 TEST_AND_RETURN_FALSE(utils::MakeTempFile(
119 "CrAU_temp_data.ordered.XXXXXX",
120 &ordered_blobs_path,
121 nullptr));
122 ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
123 TEST_AND_RETURN_FALSE(ReorderDataBlobs(data_blobs_path, ordered_blobs_path));
124
125 // Copy the operations from the aops_map_ to the manifest.
126 manifest_.clear_install_operations();
127 manifest_.clear_kernel_install_operations();
128 for (PartitionName name : partition_disk_order_) {
129 for (const AnnotatedOperation& aop : aops_map_[name]) {
130 if (name == PartitionName::kKernel) {
131 *manifest_.add_kernel_install_operations() = aop.op;
132 } else {
133 *manifest_.add_install_operations() = aop.op;
134 }
135 }
136 }
137
138 // Check that install op blobs are in order.
139 uint64_t next_blob_offset = 0;
140 {
141 for (int i = 0; i < (manifest_.install_operations_size() +
142 manifest_.kernel_install_operations_size()); i++) {
Alex Deymoa12ee112015-08-12 22:19:32 -0700143 InstallOperation* op = i < manifest_.install_operations_size()
144 ? manifest_.mutable_install_operations(i)
145 : manifest_.mutable_kernel_install_operations(
146 i - manifest_.install_operations_size());
Alex Deymo14158572015-06-13 03:37:08 -0700147 if (op->has_data_offset()) {
148 if (op->data_offset() != next_blob_offset) {
149 LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
150 << next_blob_offset;
151 }
152 next_blob_offset += op->data_length();
153 }
154 }
155 }
156
157 // Signatures appear at the end of the blobs. Note the offset in the
158 // manifest_.
159 if (!private_key_path.empty()) {
160 uint64_t signature_blob_length = 0;
161 TEST_AND_RETURN_FALSE(
162 PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
163 &signature_blob_length));
164 AddSignatureOp(next_blob_offset, signature_blob_length, &manifest_);
165 }
166
167 // Serialize protobuf
168 string serialized_manifest;
169 TEST_AND_RETURN_FALSE(manifest_.AppendToString(&serialized_manifest));
170
171 LOG(INFO) << "Writing final delta file header...";
172 DirectFileWriter writer;
173 TEST_AND_RETURN_FALSE_ERRNO(writer.Open(payload_file.c_str(),
174 O_WRONLY | O_CREAT | O_TRUNC,
175 0644) == 0);
176 ScopedFileWriterCloser writer_closer(&writer);
177
178 // Write header
179 TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
180
181 // Write major version number
182 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kMajorVersionNumber));
183
184 // Write protobuf length
185 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
186 serialized_manifest.size()));
187
188 // Write protobuf
189 LOG(INFO) << "Writing final delta file protobuf... "
190 << serialized_manifest.size();
191 TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
192 serialized_manifest.size()));
193
194 // Append the data blobs
195 LOG(INFO) << "Writing final delta file data blobs...";
196 int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
197 ScopedFdCloser blobs_fd_closer(&blobs_fd);
198 TEST_AND_RETURN_FALSE(blobs_fd >= 0);
199 for (;;) {
200 vector<char> buf(1024 * 1024);
201 ssize_t rc = read(blobs_fd, buf.data(), buf.size());
202 if (0 == rc) {
203 // EOF
204 break;
205 }
206 TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
207 TEST_AND_RETURN_FALSE(writer.Write(buf.data(), rc));
208 }
209
210 // Write signature blob.
211 if (!private_key_path.empty()) {
212 LOG(INFO) << "Signing the update...";
213 chromeos::Blob signature_blob;
214 TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
215 payload_file,
216 vector<string>(1, private_key_path),
217 &signature_blob));
218 TEST_AND_RETURN_FALSE(writer.Write(signature_blob.data(),
219 signature_blob.size()));
220 }
221
222 *medatata_size_out =
223 strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
224 ReportPayloadUsage(*medatata_size_out);
225 return true;
226}
227
228bool PayloadFile::ReorderDataBlobs(
229 const string& data_blobs_path,
230 const string& new_data_blobs_path) {
231 int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
232 TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
233 ScopedFdCloser in_fd_closer(&in_fd);
234
235 DirectFileWriter writer;
236 TEST_AND_RETURN_FALSE(
237 writer.Open(new_data_blobs_path.c_str(),
238 O_WRONLY | O_TRUNC | O_CREAT,
239 0644) == 0);
240 ScopedFileWriterCloser writer_closer(&writer);
241 uint64_t out_file_size = 0;
242
243 for (PartitionName name : partition_disk_order_) {
244 for (AnnotatedOperation& aop : aops_map_[name]) {
245 if (!aop.op.has_data_offset())
246 continue;
247 CHECK(aop.op.has_data_length());
248 chromeos::Blob buf(aop.op.data_length());
249 ssize_t rc = pread(in_fd, buf.data(), buf.size(), aop.op.data_offset());
250 TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
251
252 // Add the hash of the data blobs for this operation
253 TEST_AND_RETURN_FALSE(AddOperationHash(&aop.op, buf));
254
255 aop.op.set_data_offset(out_file_size);
256 TEST_AND_RETURN_FALSE(writer.Write(buf.data(), buf.size()));
257 out_file_size += buf.size();
258 }
259 }
260 return true;
261}
262
Alex Deymoa12ee112015-08-12 22:19:32 -0700263bool PayloadFile::AddOperationHash(InstallOperation* op,
264 const chromeos::Blob& buf) {
Alex Deymo14158572015-06-13 03:37:08 -0700265 OmahaHashCalculator hasher;
266 TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
267 TEST_AND_RETURN_FALSE(hasher.Finalize());
268 const chromeos::Blob& hash = hasher.raw_hash();
269 op->set_data_sha256_hash(hash.data(), hash.size());
270 return true;
271}
272
273void PayloadFile::ReportPayloadUsage(uint64_t metadata_size) const {
274 vector<DeltaObject> objects;
275 off_t total_size = 0;
276
277 for (PartitionName name : partition_disk_order_) {
278 const auto& partition_aops = aops_map_.find(name);
279 if (partition_aops == aops_map_.end())
280 continue;
281 for (const AnnotatedOperation& aop : partition_aops->second) {
282 objects.push_back(DeltaObject(aop.name,
283 aop.op.type(),
284 aop.op.data_length()));
285 total_size += aop.op.data_length();
286 }
287 }
288
289 objects.push_back(DeltaObject("<manifest-metadata>",
290 -1,
291 metadata_size));
292 total_size += metadata_size;
293
294 std::sort(objects.begin(), objects.end());
295
296 static const char kFormatString[] = "%6.2f%% %10jd %-10s %s\n";
297 for (const DeltaObject& object : objects) {
298 fprintf(stderr, kFormatString,
299 object.size * 100.0 / total_size,
300 static_cast<intmax_t>(object.size),
301 object.type >= 0 ? kInstallOperationTypes[object.type] : "-",
302 object.name.c_str());
303 }
304 fprintf(stderr, kFormatString,
305 100.0, static_cast<intmax_t>(total_size), "", "<total>");
306}
307
308void AddSignatureOp(uint64_t signature_blob_offset,
309 uint64_t signature_blob_length,
310 DeltaArchiveManifest* manifest) {
311 LOG(INFO) << "Making room for signature in file";
312 manifest->set_signatures_offset(signature_blob_offset);
313 LOG(INFO) << "set? " << manifest->has_signatures_offset();
314 // Add a dummy op at the end to appease older clients
Alex Deymoa12ee112015-08-12 22:19:32 -0700315 InstallOperation* dummy_op = manifest->add_kernel_install_operations();
316 dummy_op->set_type(InstallOperation::REPLACE);
Alex Deymo14158572015-06-13 03:37:08 -0700317 dummy_op->set_data_offset(signature_blob_offset);
318 manifest->set_signatures_offset(signature_blob_offset);
319 dummy_op->set_data_length(signature_blob_length);
320 manifest->set_signatures_size(signature_blob_length);
321 Extent* dummy_extent = dummy_op->add_dst_extents();
322 // Tell the dummy op to write this data to a big sparse hole
323 dummy_extent->set_start_block(kSparseHole);
324 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
325 kBlockSize);
326}
327
328} // namespace chromeos_update_engine