update_engine

在update_engine-DownloadAction(一)中对DownloadAction介绍到了DeltaPerformer的Write方法。下面开始介绍Write方法。

src/system/update_engine/payload_consumer/delta_performer.cc

1 1 bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) { 2 2 *error = ErrorCode::kSuccess; 3 3 4 4 const char* c_bytes = reinterpret_cast<const char*>(bytes); 5 5 6 6 // Update the total byte downloaded count and the progress logs. 7 7 total_bytes_received_ += count; 8 8 UpdateOverallProgress(false, "Completed "); //更新进度包括了已经应用的操作数,下载的数据量,以及总的进度 9 9 10 10 while (!manifest_valid_) { //manifest_valid_的初始值为false 11 11 // Read data up to the needed limit; this is either maximium payload header 12 12 // size, or the full metadata size (once it becomes known). 13 13 const bool do_read_header = !IsHeaderParsed(); //是否解析过Header 14 14 CopyDataToBuffer(&c_bytes, &count, //将数据拷贝到缓存区server中 15 15 (do_read_header ? kMaxPayloadHeaderSize : 16 16 metadata_size_ + metadata_signature_size_)); 17 17 18 18 MetadataParseResult result = ParsePayloadMetadata(buffer_, error); //解析元数据 19 19 if (result == kMetadataParseError) 20 20 return false; 21 21 if (result == kMetadataParseInsufficientData) { 22 22 // If we just processed the header, make an attempt on the manifest. 23 23 if (do_read_header && IsHeaderParsed()) 24 24 continue; 25 25 26 26 return true; 27 27 } 28 28 29 29 // Checks the integrity of the payload manifest. 30 30 if ((*error = ValidateManifest()) != ErrorCode::kSuccess) //验证Manifest 31 31 return false; 32 32 manifest_valid_ = true; 33 33 34 34 // Clear the download buffer. 35 35 DiscardBuffer(false, metadata_size_); //清除缓存区 36 36 37 37 // This populates |partitions_| and the |install_plan.partitions| with the 38 38 // list of partitions from the manifest. 39 39 if (!ParseManifestPartitions(error)) //解析Manifest中的Partitions的信息 40 40 return false; 41 41 42 42 // |install_plan.partitions| was filled in, nothing need to be done here if 43 43 // the payload was already applied, returns false to terminate http fetcher, 44 44 // but keep |error| as ErrorCode::kSuccess. 45 45 if (payload_->already_applied) //检查当前payload_是否已经被应用 46 46 return false; 47 47 48 48 num_total_operations_ = 0; 49 49 for (const auto& partition : partitions_) { 50 50 num_total_operations_ += partition.operations_size(); //计算总的操作数 51 51 acc_num_operations_.push_back(num_total_operations_); //将每次计算的操作数放入到集合中,这样做的意义有能够根据操作数来判断是哪个 52 52 } //分区要进行操作,以及是该分区的第几个操作 53 53 54 54 LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize, 55 55 metadata_size_)) 56 56 << "Unable to save the manifest metadata size."; 57 57 LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestSignatureSize, 58 58 metadata_signature_size_)) 59 59 << "Unable to save the manifest signature size."; 60 60 61 61 if (!PrimeUpdateState()) { /更新主要的状态,包含了block_size_,next_operation_num等 62 62 *error = ErrorCode::kDownloadStateInitializationError; 63 63 LOG(ERROR) << "Unable to prime the update state."; 64 64 return false; 65 65 } 66 66 67 67 if (!OpenCurrentPartition()) { //打开当前的分区,包括source_slot和target_slot的,为升级做准备 68 68 *error = ErrorCode::kInstallDeviceOpenError; 69 69 return false; 70 70 } 71 71 72 72 if (next_operation_num_ > 0) 73 73 UpdateOverallProgress(true, "Resuming after "); 74 74 LOG(INFO) << "Starting to apply update payload operations"; 75 75 } 76 76 77 77 while (next_operation_num_ < num_total_operations_) { //开始进行更新 78 78 // Check if we should cancel the current attempt for any reason. 79 79 // In this case, *error will have already been populated with the reason 80 80 // why we're canceling. 81 81 if (download_delegate_ && download_delegate_->ShouldCancel(error)) //目前什么都没做,直接返回了false 82 82 return false; 83 83 84 84 // We know there are more operations to perform because we didn't reach the 85 85 // |num_total_operations_| limit yet. 86 86 while (next_operation_num_ >= acc_num_operations_[current_partition_]) { //说明了当前分区已经更新完成,需要更新下一个分区 87 87 CloseCurrentPartition(); //关闭当前分区 88 88 current_partition_++; //切换到下一个分区 89 89 if (!OpenCurrentPartition()) { //打开 90 90 *error = ErrorCode::kInstallDeviceOpenError; 91 91 return false; 92 92 } 93 93 } 94 94 const size_t partition_operation_num = next_operation_num_ - ( 95 95 current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0); //计算出当前分区将要应用的操作数 96 96 97 97 const InstallOperation& op = 98 98 partitions_[current_partition_].operations(partition_operation_num); //获取到操作的类型 99 99 100100 CopyDataToBuffer(&c_bytes, &count, op.data_length()); //将该操作对应的数据放到缓存区中 101101 102102 // Check whether we received all of the next operation's data payload. 103103 if (!CanPerformInstallOperation(op)) //验证该操作是否能够进行,主要就是看该操作对应的数据是否已经全部都下载完了 104104 return true; 105105 106106 // Validate the operation only if the metadata signature is present. 107107 // Otherwise, keep the old behavior. This serves as a knob to disable 108108 // the validation logic in case we find some regression after rollout. 109109 // NOTE: If hash checks are mandatory and if metadata_signature is empty, 110110 // we would have already failed in ParsePayloadMetadata method and thus not 111111 // even be here. So no need to handle that case again here. 112112 if (!payload_->metadata_signature.empty()) { 113113 // Note: Validate must be called only if CanPerformInstallOperation is 114114 // called. Otherwise, we might be failing operations before even if there 115115 // isn't sufficient data to compute the proper hash. 116116 *error = ValidateOperationHash(op); //校验操作对应数据的hash值是否正确 117117 if (*error != ErrorCode::kSuccess) { 118118 if (install_plan_->hash_checks_mandatory) { 119119 LOG(ERROR) << "Mandatory operation hash check failed"; 120120 return false; 121121 } 122122 123123 // For non-mandatory cases, just send a UMA stat. 124124 LOG(WARNING) << "Ignoring operation validation errors"; 125125 *error = ErrorCode::kSuccess; 126126 } 127127 } 128128 129129 // Makes sure we unblock exit when this operation completes. 130130 ScopedTerminatorExitUnblocker exit_unblocker = 131131 ScopedTerminatorExitUnblocker(); // Avoids a compiler unused var bug. 132132 133133 bool op_result; 134134 switch (op.type()) { //根据操作的类型执行对应的操作 135135 case InstallOperation::REPLACE: 136136 case InstallOperation::REPLACE_BZ: 137137 case InstallOperation::REPLACE_XZ: 138138 op_result = PerformReplaceOperation(op); 139139 break; 140140 case InstallOperation::ZERO: 141141 case InstallOperation::DISCARD: 142142 op_result = PerformZeroOrDiscardOperation(op); 143143 break; 144144 case InstallOperation::MOVE: 145145 op_result = PerformMoveOperation(op); 146146 break; 147147 case InstallOperation::BSDIFF: 148148 op_result = PerformBsdiffOperation(op); 149149 break; 150150 case InstallOperation::SOURCE_COPY: 151151 op_result = PerformSourceCopyOperation(op, error); 152152 break; 153153 case InstallOperation::SOURCE_BSDIFF: 154154 op_result = PerformSourceBsdiffOperation(op, error); 155155 break; 156156 case InstallOperation::IMGDIFF: 157157 // TODO(deymo): Replace with PUFFIN operation. 158158 op_result = false; 159159 break; 160160 default: 161161 op_result = false; 162162 } 163163 if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error)) //对处理结果进行打印 164164 return false; 165165 166166 next_operation_num_++; 167167 UpdateOverallProgress(false, "Completed "); 168168 CheckpointUpdateProgress(); //保存更新进度,类似于断点能够进行保存 169169 } 170170 171171 // In major version 2, we don't add dummy operation to the payload. 172172 // If we already extracted the signature we should skip this step. 173 174173 if (major_payload_version_ == kBrilloMajorPayloadVersion && 175174 manifest_.has_signatures_offset() && manifest_.has_signatures_size() && 176175 signatures_message_data_.empty()) { 177176 if (manifest_.signatures_offset() != buffer_offset_) { 178177 LOG(ERROR) << "Payload signatures offset points to blob offset " 179178 << manifest_.signatures_offset() 180179 << " but signatures are expected at offset " 181180 << buffer_offset_; 182181 *error = ErrorCode::kDownloadPayloadVerificationError; 183182 return false; 184183 } 185184 CopyDataToBuffer(&c_bytes, &count, manifest_.signatures_size()); 186185 // Needs more data to cover entire signature. 187186 if (buffer_.size() < manifest_.signatures_size()) 188187 return true; 189188 if (!ExtractSignatureMessage()) { //获取升级文件中数据区域的签名 190189 LOG(ERROR) << "Extract payload signature failed."; 191190 *error = ErrorCode::kDownloadPayloadVerificationError; 192191 return false; 193192 } 194193 DiscardBuffer(true, 0); 195194 // Since we extracted the SignatureMessage we need to advance the 196195 // checkpoint, otherwise we would reload the signature and try to extract 197196 // it again. 198197 CheckpointUpdateProgress(); 199198 } 200199 201200 return true; 202201 }

 这个方法乍一看上去内容特别的多,而且如果对升级文件没有一个了解的情况下分析这段代码会有一点点困难,但是当跨过这个困难的时候就会对升级文件的结构有一个了解。要想了解升级文件的结构可以直接分析升级文件,但是在android引入A/B升级之后,升级文件是纯二进制的文件,而且还被加了密。分析起来难度也比较大,当然我们也可以分析代码中是如何解析的,根据解析我们就能够获取到升级文件的结构。另外在A/B升级中,它应用更新的流程就是下载->解析->验证->应用。这里的下载指的就是将数据加载到内存中,并且是边下载边更新,更新完之后就会把数据从内存中移除。下面是分析代码所得到升级文件的结构。

升级文件的结构

magic:是用于校验数据在内存中的地址偏移量是否正确。假设我们预期从地址0到3存放A,B,C,D可是当计算存在问题时,应该得到0的时候我们得到的是1,那么就会在1到4存放A,B,C,D,而我们再从0开始访问就会有问题。如果在内存的开始部分加入一个magic,当我们从0开始访问的时候,就先根据定义好的magic判断数据在内存中是否发生偏移错误。也就存放数据的时候我们存放magic,A,B,C,D正确的结果是0到4,但是却放到了1到5,这个时候我们依然去用0开始访问,但是我们首先会检验在0上的magic和预期的一样,如果一样则说明没有发生偏移错误,可以继续访问,如果不一样则说明偏移错误,之后可以进行相应的处理。

delta version:是差分版本,也就是update_engine的版本号

manifest_size: 代表manifest的大小,manifest意为清单文件,系统如何升级也是根据manifest来做的。

metadata_signaturesize:代表了元数据签名的大小。可以将magic,dleta version,manifest_size,metadata_signaturesize以及manifest[]称为元数据。

manifest[]: 主要的清单文件,记录了各个分区的更新的操作,以及数据信息等

metadata_signaturesize_message : 元数据的签名,而且也是经过加密的

data: 用于更新的数据

data_messgage:为data的签名信息。在kBrilloMajorPayloadVersion 这个版本中才会有。在A/B更新出现后,一共出现了两个版本一个kChromeOSMajorPayloadVersion一个kBrilloMajorPayloadVersion,kBrilloMajorPayloadVersion这个版本为新版本,也是Android8.0中使用的。

当有了这些了解后再来分析Write方法是就会简单很多。在Write中主要做的事情为:

1. ParsePayloadMetadata解析元数据。来看一下是如何解析的。

1 1 DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata( 2 2 const brillo::Blob& payload, ErrorCode* error) { 3 3 *error = ErrorCode::kSuccess; 4 4 uint64_t manifest_offset; 5 5 6 6 if (!IsHeaderParsed()) { //没有解析过 7 7 // Ensure we have data to cover the major payload version. 8 8 if (payload.size() < kDeltaManifestSizeOffset) //kDeltaManifestSizeOffset=kDeltaVersionOffset + kDeltaVersionSize 9 9 return kMetadataParseInsufficientData; //没有将magic和delta version加载完 10 10 11 11 // Validate the magic string. 12 12 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) { //校验magic, 13 13 LOG(ERROR) << "Bad payload format -- invalid delta magic."; 14 14 *error = ErrorCode::kDownloadInvalidMetadataMagicString; 15 15 return kMetadataParseError; 16 16 } 17 17 18 18 // Extract the payload version from the metadata. 19 19 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize, 20 20 "Major payload version size mismatch"); 21 21 memcpy(&major_payload_version_, 22 22 &payload[kDeltaVersionOffset], //保存DeltaVesion 23 23 kDeltaVersionSize); 24 24 // switch big endian to host 25 25 major_payload_version_ = be64toh(major_payload_version_); //转换为主机字节序 26 26 27 27 if (major_payload_version_ != supported_major_version_ && //判断版本号是否正确 28 28 major_payload_version_ != kChromeOSMajorPayloadVersion) { 29 29 LOG(ERROR) << "Bad payload format -- unsupported payload version: " 30 30 << major_payload_version_; 31 31 *error = ErrorCode::kUnsupportedMajorPayloadVersion; 32 32 return kMetadataParseError; 33 33 } 34 34 35 35 // Get the manifest offset now that we have payload version. 36 36 if (!GetManifestOffset(&manifest_offset)) { //获取指向manifest的地址偏移量 37 37 *error = ErrorCode::kUnsupportedMajorPayloadVersion; 38 38 return kMetadataParseError; 39 39 } 40 40 // Check again with the manifest offset. 41 41 if (payload.size() < manifest_offset) //判断manifset之前的数据是否都已经加载到了内存中 42 42 return kMetadataParseInsufficientData; 43 43 44 44 // Next, parse the manifest size. 45 45 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize, 46 46 "manifest_size size mismatch"); 47 47 memcpy(&manifest_size_, //保存manifest的大小 48 48 &payload[kDeltaManifestSizeOffset], 49 49 kDeltaManifestSizeSize); 50 50 manifest_size_ = be64toh(manifest_size_); // 转换为主机字节序 51 51 52 52 if (GetMajorVersion() == kBrilloMajorPayloadVersion) { //如果是新版本 53 53 // Parse the metadata signature size. 54 54 static_assert(sizeof(metadata_signature_size_) == 55 55 kDeltaMetadataSignatureSizeSize, 56 56 "metadata_signature_size size mismatch"); 57 57 uint64_t metadata_signature_size_offset; 58 58 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) { //获取metadata_signature_size数据的偏移量 59 59 *error = ErrorCode::kError; 60 60 return kMetadataParseError; 61 61 } 62 62 memcpy(&metadata_signature_size_, //保存元数据的大小 63 63 &payload[metadata_signature_size_offset], 64 64 kDeltaMetadataSignatureSizeSize); 65 65 metadata_signature_size_ = be32toh(metadata_signature_size_); //转换为主机字节序 66 66 } 67 67 68 68 // If the metadata size is present in install plan, check for it immediately 69 69 // even before waiting for that many number of bytes to be downloaded in the 70 70 // payload. This will prevent any attack which relies on us downloading data 71 71 // beyond the expected metadata size. 72 72 metadata_size_ = manifest_offset + manifest_size_; //计算元数据的大小 73 73 if (install_plan_->hash_checks_mandatory) { //进行强制性检查,增加安全性 74 74 if (payload_->metadata_size != metadata_size_) { 75 75 LOG(ERROR) << "Mandatory metadata size in Omaha response (" 76 76 << payload_->metadata_size 77 77 << ") is missing/incorrect, actual = " << metadata_size_; 78 78 *error = ErrorCode::kDownloadInvalidMetadataSize; 79 79 return kMetadataParseError; 80 80 } 81 81 } 82 82 } 83 83 84 84 // Now that we have validated the metadata size, we should wait for the full 85 85 // metadata and its signature (if exist) to be read in before we can parse it. 86 86 if (payload.size() < metadata_size_ + metadata_signature_size_) //检查metadata_signature_message是否已经加载到了内存 87 87 return kMetadataParseInsufficientData; 88 88 89 89 // Log whether we validated the size or simply trusting what's in the payload 90 90 // here. This is logged here (after we received the full metadata data) so 91 91 // that we just log once (instead of logging n times) if it takes n 92 92 // DeltaPerformer::Write calls to download the full manifest. 93 93 if (payload_->metadata_size == metadata_size_) { //payload_中也保存了metadata_size,进行比对一下 94 94 LOG(INFO) << "Manifest size in payload matches expected value from Omaha"; 95 95 } else { 96 96 // For mandatory-cases, we'd have already returned a kMetadataParseError 97 97 // above. We'll be here only for non-mandatory cases. Just send a UMA stat. 98 98 LOG(WARNING) << "Ignoring missing/incorrect metadata size (" 99 99 << payload_->metadata_size 100100 << ") in Omaha response as validation is not mandatory. " 101101 << "Trusting metadata size in payload = " << metadata_size_; 102102 } 103103 104104 // We have the full metadata in |payload|. Verify its integrity 105105 // and authenticity based on the information we have in Omaha response. 106106 *error = ValidateMetadataSignature(payload); //验证元数据的签名 107107 if (*error != ErrorCode::kSuccess) { 108108 if (install_plan_->hash_checks_mandatory) { 109109 // The autoupdate_CatchBadSignatures test checks for this string 110110 // in log-files. Keep in sync. 111111 LOG(ERROR) << "Mandatory metadata signature validation failed"; 112112 return kMetadataParseError; 113113 } 114114 115115 // For non-mandatory cases, just send a UMA stat. 116116 LOG(WARNING) << "Ignoring metadata signature validation failures"; 117117 *error = ErrorCode::kSuccess; 118118 } 119119 120120 if (!GetManifestOffset(&manifest_offset)) { //获取manifest_offset 121121 *error = ErrorCode::kUnsupportedMajorPayloadVersion; 122122 return kMetadataParseError; 123123 } 124124 // The payload metadata is deemed valid, it's safe to parse the protobuf. 125125 if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) { //解析manifest 126126 LOG(ERROR) << "Unable to parse manifest in update file."; 127127 *error = ErrorCode::kDownloadManifestParseError; 128128 return kMetadataParseError; 129129 } 130130 131131 manifest_parsed_ = true; 132132 return kMetadataParseSuccess; 133133 }

可以看到整个解析的过程也比较简单了。接下来着重看一下ValidateMetadataSignature的实现

1 1 ErrorCode DeltaPerformer::ValidateMetadataSignature( 2 2 const brillo::Blob& payload) { 3 3 if (payload.size() < metadata_size_ + metadata_signature_size_) 4 4 return ErrorCode::kDownloadMetadataSignatureError; //判断签名是否已经加载到了内存中 5 5 6 6 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob; 7 7 if (!payload_->metadata_signature.empty()) { //payload_中已经保存了metadata_signature 8 8 // Convert base64-encoded signature to raw bytes. 9 9 if (!brillo::data_encoding::Base64Decode(payload_->metadata_signature, 1010 &metadata_signature_blob)) { //先对签名进行Base64的简码 1111 LOG(ERROR) << "Unable to decode base64 metadata signature: " 1212 << payload_->metadata_signature; 1313 return ErrorCode::kDownloadMetadataSignatureError; 1414 } 1515 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) { 1616 metadata_signature_protobuf_blob.assign( //没有保存就从内存中加载 1717 payload.begin() + metadata_size_, 1818 payload.begin() + metadata_size_ + metadata_signature_size_); 1919 } 2020 2121 if (metadata_signature_blob.empty() && 2222 metadata_signature_protobuf_blob.empty()) { //没有metadata_signature 2323 if (install_plan_->hash_checks_mandatory) { 2424 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha " 2525 << "response and payload."; 2626 return ErrorCode::kDownloadMetadataSignatureMissingError; 2727 } 2828 2929 LOG(WARNING) << "Cannot validate metadata as the signature is empty"; 3030 return ErrorCode::kSuccess; 3131 } 3232 3333 // See if we should use the public RSA key in the Omaha response. 3434 base::FilePath path_to_public_key(public_key_path_); 3535 base::FilePath tmp_key; 3636 if (GetPublicKeyFromResponse(&tmp_key)) //检查install_plan_中是否已经带了公钥 3737 path_to_public_key = tmp_key; 3838 ScopedPathUnlinker tmp_key_remover(tmp_key.value()); 3939 if (tmp_key.empty()) 4040 tmp_key_remover.set_should_remove(false); 4141 4242 LOG(INFO) << "Verifying metadata hash signature using public key: " 4343 << path_to_public_key.value(); 4444 4545 brillo::Blob calculated_metadata_hash; 4646 if (!HashCalculator::RawHashOfBytes( //根据元数据计算一个hash 4747 payload.data(), metadata_size_, &calculated_metadata_hash)) { 4848 LOG(ERROR) << "Unable to compute actual hash of manifest"; 4949 return ErrorCode::kDownloadMetadataSignatureVerificationError; 5050 } 5151 5252 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash); //对hash进行填充 5353 if (calculated_metadata_hash.empty()) { 5454 LOG(ERROR) << "Computed actual hash of metadata is empty."; 5555 return ErrorCode::kDownloadMetadataSignatureVerificationError; 5656 } 5757 5858 if (!metadata_signature_blob.empty()) { //payload_中已经保存了签名 5959 brillo::Blob expected_metadata_hash; 6060 if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob, //使用公钥对其进行解密 6161 path_to_public_key.value(), 6262 &expected_metadata_hash)) { 6363 LOG(ERROR) << "Unable to compute expected hash from metadata signature"; 6464 return ErrorCode::kDownloadMetadataSignatureError; 6565 } 6666 if (calculated_metadata_hash != expected_metadata_hash) { //判断保存的和自己算出来的签名是否相同 6767 LOG(ERROR) << "Manifest hash verification failed. Expected hash = "; 6868 utils::HexDumpVector(expected_metadata_hash); 6969 LOG(ERROR) << "Calculated hash = "; 7070 utils::HexDumpVector(calculated_metadata_hash); 7171 return ErrorCode::kDownloadMetadataSignatureMismatch; 7272 } 7373 } else { //在升级数据中含有签名信息时,对签名的校验 7474 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob, 7575 path_to_public_key.value(), 7676 calculated_metadata_hash)) { 7777 LOG(ERROR) << "Manifest hash verification failed."; 7878 return ErrorCode::kDownloadMetadataSignatureMismatch; 7979 } 8080 } 8181 8282 // The autoupdate_CatchBadSignatures test checks for this string in 8383 // log-files. Keep in sync. 8484 LOG(INFO) << "Metadata hash signature matches value in Omaha response."; 8585 return ErrorCode::kSuccess; 8686 }

这个方法主要说明了metadata_signature签名的验证机制,其中有一个payload_,是在DeltaPerformer构造函数中赋的值。接下来在分析manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_),在初看到这行代码的时候,花了很长时间也没有找到ParseFromArray的实现。DeltaArchiveManifest类也没有找到对应的C++类,但是却找到了update_metadata_pb2.py和update_metadata.proto。update_metadata.proto的内容如下

src/system/update_engine/update_metadata.proto

1 1 message Extent { 2 2 optional uint64 start_block = 1; 3 3 optional uint64 num_blocks = 2; 4 4 } 5 5 6 6 message Signatures { 7 7 message Signature { 8 8 optional uint32 version = 1; 9 9 optional bytes data = 2; 10 10 } 11 11 repeated Signature signatures = 1; 12 12 } 13 13 14 14 message PartitionInfo { 15 15 optional uint64 size = 1; 16 16 optional bytes hash = 2; 17 17 } 18 18 19 19 // Describe an image we are based on in a human friendly way. 20 20 // Examples: 21 21 // dev-channel, x86-alex, 1.2.3, mp-v3 22 22 // nplusone-channel, x86-alex, 1.2.4, mp-v3, dev-channel, 1.2.3 23 23 // 24 24 // All fields will be set, if this message is present. 25 25 message ImageInfo { 26 26 optional string board = 1; 27 27 optional string key = 2; 28 28 optional string channel = 3; 29 29 optional string version = 4; 30 30 31 31 // If these values aren't present, they should be assumed to match 32 32 // the equivalent value above. They are normally only different for 33 33 // special image types such as nplusone images. 34 34 optional string build_channel = 5; 35 35 optional string build_version = 6; 36 36 } 37 37 38 38 message InstallOperation { 39 39 enum Type { 40 40 REPLACE = 0; // Replace destination extents w/ attached data 41 41 REPLACE_BZ = 1; // Replace destination extents w/ attached bzipped data 42 42 MOVE = 2; // Move source extents to destination extents 43 43 BSDIFF = 3; // The data is a bsdiff binary diff 44 44 45 45 // On minor version 2 or newer, these operations are supported: 46 46 SOURCE_COPY = 4; // Copy from source to target partition 47 47 SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition 48 48 49 49 // On minor version 3 or newer and on major version 2 or newer, these 50 50 // operations are supported: 51 51 ZERO = 6; // Write zeros in the destination. 52 52 DISCARD = 7; // Discard the destination blocks, reading as undefined. 53 53 REPLACE_XZ = 8; // Replace destination extents w/ attached xz data. 54 54 55 55 // On minor version 4 or newer, these operations are supported: 56 56 IMGDIFF = 9; // The data is in imgdiff format. 57 57 } 58 58 required Type type = 1; 59 59 // The offset into the delta file (after the protobuf) 60 60 // where the data (if any) is stored 61 61 optional uint32 data_offset = 2; 62 62 // The length of the data in the delta file 63 63 optional uint32 data_length = 3; 64 64 65 65 // Ordered list of extents that are read from (if any) and written to. 66 66 repeated Extent src_extents = 4; 67 67 // Byte length of src, equal to the number of blocks in src_extents * 68 68 // block_size. It is used for BSDIFF, because we need to pass that 69 69 // external program the number of bytes to read from the blocks we pass it. 70 70 // This is not used in any other operation. 71 71 optional uint64 src_length = 5; 72 72 73 73 repeated Extent dst_extents = 6; 74 74 // Byte length of dst, equal to the number of blocks in dst_extents * 75 75 // block_size. Used for BSDIFF, but not in any other operation. 76 76 optional uint64 dst_length = 7; 77 77 78 78 // Optional SHA 256 hash of the blob associated with this operation. 79 79 // This is used as a primary validation for http-based downloads and 80 80 // as a defense-in-depth validation for https-based downloads. If 81 81 // the operation doesn't refer to any blob, this field will have 82 82 // zero bytes. 83 83 optional bytes data_sha256_hash = 8; 84 84 85 85 // Indicates the SHA 256 hash of the source data referenced in src_extents at 86 86 // the time of applying the operation. If present, the update_engine daemon 87 87 // MUST read and verify the source data before applying the operation. 88 88 optional bytes src_sha256_hash = 9; 89 89 } 90 90 91 91 // Describes the update to apply to a single partition. 92 92 message PartitionUpdate { 93 93 // A platform-specific name to identify the partition set being updated. For 94 94 // example, in Chrome OS this could be "ROOT" or "KERNEL". 95 95 required string partition_name = 1; 96 96 97 97 // Whether this partition carries a filesystem with post-install program that 98 98 // must be run to finalize the update process. See also |postinstall_path| and 99 99 // |filesystem_type|. 100100 optional bool run_postinstall = 2; 101101 102102 // The path of the executable program to run during the post-install step, 103103 // relative to the root of this filesystem. If not set, the default "postinst" 104104 // will be used. This setting is only used when |run_postinstall| is set and 105105 // true. 106106 optional string postinstall_path = 3; 107107 108108 // The filesystem type as passed to the mount(2) syscall when mounting the new 109109 // filesystem to run the post-install program. If not set, a fixed list of 110110 // filesystems will be attempted. This setting is only used if 111111 // |run_postinstall| is set and true. 112112 optional string filesystem_type = 4; 113113 114114 // If present, a list of signatures of the new_partition_info.hash signed with 115115 // different keys. If the update_engine daemon requires vendor-signed images 116116 // and has its public key installed, one of the signatures should be valid 117117 // for /postinstall to run. 118118 repeated Signatures.Signature new_partition_signature = 5; 119119 120120 optional PartitionInfo old_partition_info = 6; 121121 optional PartitionInfo new_partition_info = 7; 122122 123123 // The list of operations to be performed to apply this PartitionUpdate. The 124124 // associated operation blobs (in operations[i].data_offset, data_length) 125125 // should be stored contiguously and in the same order. 126126 repeated InstallOperation operations = 8; 127127 128128 // Whether a failure in the postinstall step for this partition should be 129129 // ignored. 130130 optional bool postinstall_optional = 9; 131131 } 132132 133133 message DeltaArchiveManifest { 134134 // Only present in major version = 1. List of install operations for the 135135 // kernel and rootfs partitions. For major version = 2 see the |partitions| 136136 // field. 137137 repeated InstallOperation install_operations = 1; 138138 repeated InstallOperation kernel_install_operations = 2; 139139 140140 // (At time of writing) usually 4096 141141 optional uint32 block_size = 3 [default = 4096]; 142142 143143 // If signatures are present, the offset into the blobs, generally 144144 // tacked onto the end of the file, and the length. We use an offset 145145 // rather than a bool to allow for more flexibility in future file formats. 146146 // If either is absent, it means signatures aren't supported in this 147147 // file. 148148 optional uint64 signatures_offset = 4; 149149 optional uint64 signatures_size = 5; 150150 151151 // Only present in major version = 1. Partition metadata used to validate the 152152 // update. For major version = 2 see the |partitions| field. 153153 optional PartitionInfo old_kernel_info = 6; 154154 optional PartitionInfo new_kernel_info = 7; 155155 optional PartitionInfo old_rootfs_info = 8; 156156 optional PartitionInfo new_rootfs_info = 9; 157157 158158 // old_image_info will only be present for delta images. 159159 optional ImageInfo old_image_info = 10; 160160 161161 optional ImageInfo new_image_info = 11; 162162 163163 // The minor version, also referred as "delta version", of the payload. 164164 optional uint32 minor_version = 12 [default = 0]; 165165 166166 // Only present in major version >= 2. List of partitions that will be 167167 // updated, in the order they will be updated. This field replaces the 168168 // |install_operations|, |kernel_install_operations| and the 169169 // |{old,new}_{kernel,rootfs}_info| fields used in major version = 1. This 170170 // array can have more than two partitions if needed, and they are identified 171171 // by the partition name. 172172 repeated PartitionUpdate partitions = 13; 173173 174174 // The maximum timestamp of the OS allowed to apply this payload. 175175 // Can be used to prevent downgrading the OS. 176176 optional int64 max_timestamp = 14; 177177 }

可以看出它应该就是由update_metadata_pb2.py这个脚本解析的manifest的数据格式。后来了解到这是Protobuf数据格式,是比xml和json更加高效的数据格式,采用了二进制的存储。那么其实根据DeltaArchiveManifest我们就能大体推断出Manifest中所包含的数据类型。主要就是安装更新操作的类型,数据的签名,新旧内核,rootfs,ImageInfo,分区更新等。到此ParsePayloadMetadata这个方法就算是分析完了。回到Write中继续分析,当解析完成了Manifest之后,就调用了ValidateManifest()来验证manifest。

2.ValidateManifest()来验证manifest

1 1 ErrorCode DeltaPerformer::ValidateManifest() { 2 2 // Perform assorted checks to sanity check the manifest, make sure it 3 3 // matches data from other sources, and that it is a supported version. 4 4 5 5 bool has_old_fields = 6 6 (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info()); 7 7 for (const PartitionUpdate& partition : manifest_.partitions()) { 8 8 has_old_fields = has_old_fields || partition.has_old_partition_info(); 9 9 } 1010 1111 // The presence of an old partition hash is the sole indicator for a delta 1212 // update. 1313 InstallPayloadType actual_payload_type = 1414 has_old_fields ? InstallPayloadType::kDelta : InstallPayloadType::kFull; //获取升级的类型 1515 1616 if (payload_->type == InstallPayloadType::kUnknown) { //payload_->type的默认值是KUnknown 1717 LOG(INFO) << "Detected a '" 1818 << InstallPayloadTypeToString(actual_payload_type) 1919 << "' payload."; 2020 payload_->type = actual_payload_type; 2121 } else if (payload_->type != actual_payload_type) { 2222 LOG(ERROR) << "InstallPlan expected a '" 2323 << InstallPayloadTypeToString(payload_->type) 2424 << "' payload but the downloaded manifest contains a '" 2525 << InstallPayloadTypeToString(actual_payload_type) 2626 << "' payload."; 2727 return ErrorCode::kPayloadMismatchedType; 2828 } 2929 3030 // Check that the minor version is compatible. 3131 if (actual_payload_type == InstallPayloadType::kFull) { //进行更加安全性检测 3232 if (manifest_.minor_version() != kFullPayloadMinorVersion) { 3333 LOG(ERROR) << "Manifest contains minor version " 3434 << manifest_.minor_version() 3535 << ", but all full payloads should have version " 3636 << kFullPayloadMinorVersion << "."; 3737 return ErrorCode::kUnsupportedMinorPayloadVersion; 3838 } 3939 } else { 4040 if (manifest_.minor_version() != supported_minor_version_) { 4141 LOG(ERROR) << "Manifest contains minor version " 4242 << manifest_.minor_version() 4343 << " not the supported " 4444 << supported_minor_version_; 4545 return ErrorCode::kUnsupportedMinorPayloadVersion; 4646 } 4747 } 4848 4949 if (major_payload_version_ != kChromeOSMajorPayloadVersion) { 5050 if (manifest_.has_old_rootfs_info() || //这些字段只应该在kChromeOSMajorPayloadVersion中有 5151 manifest_.has_new_rootfs_info() || 5252 manifest_.has_old_kernel_info() || 5353 manifest_.has_new_kernel_info() || 5454 manifest_.install_operations_size() != 0 || 5555 manifest_.kernel_install_operations_size() != 0) { 5656 LOG(ERROR) << "Manifest contains deprecated field only supported in " 5757 << "major payload version 1, but the payload major version is " 5858 << major_payload_version_; 5959 return ErrorCode::kPayloadMismatchedType; 6060 } 6161 } 6262 6363 if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) { //对时间戳的检测 6464 LOG(ERROR) << "The current OS build timestamp (" 6565 << hardware_->GetBuildTimestamp() 6666 << ") is newer than the maximum timestamp in the manifest (" 6767 << manifest_.max_timestamp() << ")"; 6868 return ErrorCode::kPayloadTimestampError; 6969 } 7070 7171 // TODO(garnold) we should be adding more and more manifest checks, such as 7272 // partition boundaries etc (see chromium-os:37661). 7373 7474 return ErrorCode::kSuccess; 7575 }

这个方法主要验证的了升级的类型,已经升级程序版本的正确性,最后对时间戳进行了一次校验,理论上升级包中新版本的时间戳应该比系统中当前版本的时间戳更新一些,才允许升级。对manifest进行了校验之后,在Write方法中标记manifest_valid_为true,清空缓存区后,开始对分区信息进行解析。

**3.**解析Manifest中的Partitions的信息

1 1 bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) { 2 2 if (major_payload_version_ == kBrilloMajorPayloadVersion) { 3 3 partitions_.clear(); 4 4 for (const PartitionUpdate& partition : manifest_.partitions()) { 5 5 partitions_.push_back(partition); //将partitons的信息保存到partitions中 6 6 } 7 7 manifest_.clear_partitions(); //将manifest_中的分区信息进行删除 8 8 } else if (major_payload_version_ == kChromeOSMajorPayloadVersion) { 9 9 LOG(INFO) << "Converting update information from old format."; 1010 //这部分是老版本的在使用,就先不进行分析了 1111 } 1212 1313 // Fill in the InstallPlan::partitions based on the partitions from the 1414 // payload. 1515 for (const auto& partition : partitions_) { 1616 InstallPlan::Partition install_part; 1717 install_part.name = partition.partition_name(); //分区的name 1818 install_part.run_postinstall = //postinstall 1919 partition.has_run_postinstall() && partition.run_postinstall(); 2020 if (install_part.run_postinstall) { 2121 install_part.postinstall_path = 2222 (partition.has_postinstall_path() ? partition.postinstall_path() 2323 : kPostinstallDefaultScript); 2424 install_part.filesystem_type = partition.filesystem_type(); 2525 install_part.postinstall_optional = partition.postinstall_optional(); 2626 } 2727 2828 if (partition.has_old_partition_info()) { //获取old 分区中的信息 2929 const PartitionInfo& info = partition.old_partition_info(); 3030 install_part.source_size = info.size(); 3131 install_part.source_hash.assign(info.hash().begin(), info.hash().end()); 3232 } 3333 3434 if (!partition.has_new_partition_info()) { 3535 LOG(ERROR) << "Unable to get new partition hash info on partition " 3636 << install_part.name << "."; 3737 *error = ErrorCode::kDownloadNewPartitionInfoError; 3838 return false; 3939 } 4040 const PartitionInfo& info = partition.new_partition_info(); 4141 install_part.target_size = info.size(); //新分区的信息 4242 install_part.target_hash.assign(info.hash().begin(), info.hash().end()); 4343 4444 install_plan_->partitions.push_back(install_part); //保存到install_plan_ 4545 } 4646 4747 if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) { //根据分区name,slot,获取分区的路径 4848 LOG(ERROR) << "Unable to determine all the partition devices."; 4949 *error = ErrorCode::kInstallDeviceOpenError; 5050 return false; 5151 } 5252 LogPartitionInfo(partitions_); //打印分区信息 5353 return true; 5454 }

其实解析分区主要就是将分区信息从manifest_中转移到install_plan_。在Write中最后做的就是获取操作数,获取操作类型,根据操作类型执行对应的操作,验证payload中数据的签名。其中需要注意的是关于操作数的计算和更新数据的校验。

4.关于操作数的计算,可以看下面相关的部分

1 1 num_total_operations_ = 0; 2 2 for (const auto& partition : partitions_) { 3 3 num_total_operations_ += partition.operations_size(); 4 4 acc_num_operations_.push_back(num_total_operations_); 5 5 } 6 6 7 7 while (next_operation_num_ >= acc_num_operations_[current_partition_]) { 8 8 CloseCurrentPartition(); 9 9 current_partition_++; 1010 if (!OpenCurrentPartition()) { 1111 *error = ErrorCode::kInstallDeviceOpenError; 1212 return false; 1313 } 1414 } 1515 1616 const size_t partition_operation_num = next_operation_num_ - ( 1717 current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);

假设有分区A,B,C对应的操作数为2,4,6。那么num_total_operations_ =12,acc_num_operations_.中存放的元素为2,6,12,此时执行到了第2个操作,next_operation_num_ =2,而2是等于acc_num_operations_[0]的,而存放操作的数组是从0开始的,也就是说,当next_operation_num_等于acc_num_operations_时也就是说当前分区的操作已经执行完了,应该切换到下一个分区了,最后根据next_operation_num_和acc_num_operations_计算出操作类型的索引,获取对应的操作类型。最后对于更新数据的校验是指每当应用所下载的数据的时候,都会对其进行校验,首先是保存了数据的hash值之后再根据所下载的数据计算一个hash指,进行比对,验证数据是否正确。

到这里DownloadAction的核心部分已经分析完成,下面一篇文章会分析FilesystemVerifierAction,PostinstallRunnerAction。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

sql注入

反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o