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