从上一篇我们已经知道具体的升级写入校验流程都是由InstallPlanAction,DownloadAction,FilesystemVerifierAction,PostinstallRunnerAction,这四个Action完成的,本篇主要讲解这四个的具体流程,我准备这些文件依然按照.h 和.cc进行分析
一、InstallPlanAction
二、DownloadAction
三、FilesystemVerifierAction
四、PostinstallRunnerAction
1system/update_engine/payload_consumer/install_plan.h 2 3// Basic action that only receives and sends Install Plans. 4// Can be used to construct an Install Plan to send to any other Action that 5// accept an InstallPlan. 6// 基础的action,接收和发送install plans ,通过查询代码可以发现,installplanAcion没有单独的代码 7// 实现,是其他action的父类,只用于发送和接收install_plan_ 8class InstallPlanAction : public Action{ 9 public: 10 InstallPlanAction() {} 11 explicit InstallPlanAction(const InstallPlan& install_plan): 12 install_plan_(install_plan) {} 13 //这算是具体执行Acion的方法,将install_plan_设置为install_plan_ 14 void PerformAction() override { 15 if (HasOutputPipe()) { 16 SetOutputObject(install_plan_); 17 } 18 //调用processor执行action完成的处理 19 processor_->ActionComplete(this, ErrorCode::kSuccess); 20 } 21 //定义方法可以获取install_plan_ 22 InstallPlan* install_plan() { return &install_plan_; } 23 //定义方法可以获取当前的Acion类型 24 static std::string StaticType() { return "InstallPlanAction"; } 25 std::string Type() const override { return StaticType(); } 26 //定义了管道机制中的输出和输入 27 typedef ActionTraits ::InputObjectType InputObjectType; 28 typedef ActionTraits ::OutputObjectType OutputObjectType; 29 30 private: 31 InstallPlan install_plan_; 32 33 DISALLOW_COPY_AND_ASSIGN(InstallPlanAction); 34};
我觉得这个installplanAcion相当于我们运了一车猪做火腿肠,生产线已经整好了,但是总的有个地方放这个生产线,所以整了个厂房放进去,切也好 剁也好,都放着里边,具体的代码也比较简单 只是把install_plan_作为SetOutputObject,也就是downloadAcion的输入,install_plan_就相当于那一车猪
我们先分析下system/update_engine/payload_consumer/download_action.h ,确认下大概的方法和定义参数
1// The Download Action downloads a specified url to disk. The url should point 2// to an update in a delta payload format. The payload will be piped into a 3// DeltaPerformer that will apply the delta to the disk. 4// downloadAcion会下载一个特殊的url到磁盘,这个url会指向到一个delta payload格式的升级 5// 将payload使用管道传输给DeltaPerformer,通过DeltaPerformer将delta写入到磁盘 6// 所以downloadAcion其实是具体写入到分区升级的业务 7namespace chromeos_update_engine { 8 9//这三个方法是Delegate委托 最后调用的是updateAttempterAndroid中的对应方法 10class DownloadActionDelegate { 11 public: 12 virtual ~DownloadActionDelegate() = default; 13 14 // Called periodically after bytes are received. This method will be invoked 15 // only if the DownloadAction is running. |bytes_progressed| is the number of 16 // bytes downloaded since the last call of this method, |bytes_received| 17 // the number of bytes downloaded thus far and |total| is the number of bytes 18 // expected. 19 virtual void BytesReceived(uint64_t bytes_progressed, 20 uint64_t bytes_received, 21 uint64_t total) = 0; 22 23 // Returns whether the download should be canceled, in which case the 24 // |cancel_reason| error should be set to the reason why the download was 25 // canceled. 26 virtual bool ShouldCancel(ErrorCode* cancel_reason) = 0; 27 28 // Called once the complete payload has been downloaded. Note that any errors 29 // while applying or downloading the partial payload will result in this 30 // method not being called. 31 virtual void DownloadComplete() = 0; 32}; 33 34class PrefsInterface; 35 36class DownloadAction : public InstallPlanAction, 37 public HttpFetcherDelegate { 38 public: 39 // Debugging/logging 打印当前的Action 40 static std::string StaticType() { return "DownloadAction"; } 41 42 // Takes ownership of the passed in HttpFetcher. Useful for testing. 43 // A good calling pattern is: 44 // DownloadAction(prefs, boot_contol, hardware, system_state, 45 // new WhateverHttpFetcher, false); 46 DownloadAction(PrefsInterface* prefs, 47 BootControlInterface* boot_control, 48 HardwareInterface* hardware, 49 SystemState* system_state, //传入的值为空 50 HttpFetcher* http_fetcher, 51 bool is_interactive);//传入的值为true 52 ~DownloadAction() override; 53 54 // InstallPlanAction overrides. 55 void PerformAction() override; //具体处理action 56 void SuspendAction() override; //暂停 57 void ResumeAction() override; //恢复 58 void TerminateProcessing() override; //停止 59 std::string Type() const override { return StaticType(); } 60 61 // Testing 62 void SetTestFileWriter(FileWriter* writer) { 63 writer_ = writer; 64 } 65 //获取HTTP相应的code 66 int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); } 67 68 // HttpFetcherDelegate methods (see http_fetcher.h) 重写HttpFetcherDelegate的方法 69 void ReceivedBytes(HttpFetcher* fetcher, 70 const void* bytes, size_t length) override; 71 void SeekToOffset(off_t offset) override; 72 void TransferComplete(HttpFetcher* fetcher, bool successful) override; 73 void TransferTerminated(HttpFetcher* fetcher) override; 74 //获取委托代理 75 DownloadActionDelegate* delegate() const { return delegate_; } 76 void set_delegate(DownloadActionDelegate* delegate) { 77 delegate_ = delegate; 78 } 79 80 void set_base_offset(int64_t base_offset) { base_offset_ = base_offset; } 81 // 获取http_fetcher 82 HttpFetcher* http_fetcher() { return http_fetcher_.get(); } 83 84 // Returns the p2p file id for the file being written or the empty 85 // string if we're not writing to a p2p file. 86 std::string p2p_file_id() { return p2p_file_id_; } 87 88 private: 89 // Closes the file descriptor for the p2p file being written and 90 // clears |p2p_file_id_| to indicate that we're no longer sharing 91 // the file. If |delete_p2p_file| is True, also deletes the file. 92 // If there is no p2p file descriptor, this method does nothing. 93 void CloseP2PSharingFd(bool delete_p2p_file); 94 95 // Starts sharing the p2p file. Must be called before 96 // WriteToP2PFile(). Returns True if this worked. 97 bool SetupP2PSharingFd(); 98 99 // Writes |length| bytes of payload from |data| into |file_offset| 100 // of the p2p file. Also does sanity checks; for example ensures we 101 // don't end up with a file with holes in it. 102 // 103 // This method does nothing if SetupP2PSharingFd() hasn't been 104 // called or if CloseP2PSharingFd() has been called. 105 void WriteToP2PFile(const void* data, size_t length, off_t file_offset); 106 107 // Start downloading the current payload using delta_performer. 108 // 使用delta_performer开始下载当前的payload 109 void StartDownloading(); 110 111 // The InstallPlan passed in 112 InstallPlan install_plan_; 113 114 // Pointer to the current payload in install_plan_.payloads. 115 InstallPlan::Payload* payload_{nullptr}; 116 117 // SystemState required pointers. 118 PrefsInterface* prefs_; 119 BootControlInterface* boot_control_; 120 HardwareInterface* hardware_; 121 122 // Global context for the system. 123 SystemState* system_state_; 124 125 // Pointer to the MultiRangeHttpFetcher that does the http work. 126 // 具体做这个Http业务是MultiRangeHttpFetcher 127 std::unique_ptrhttp_fetcher_; 128 129 // If |true|, the update is user initiated (vs. periodic update checks). Hence 130 // the |delta_performer_| can decide not to use O_DSYNC flag for faster 131 // update. 132 bool is_interactive_; 133 134 // The FileWriter that downloaded data should be written to. It will 135 // either point to *decompressing_file_writer_ or *delta_performer_. 136 // 具体执行写入分区操作,指向decompressing_file_writer_或者delta_performer_ 137 FileWriter* writer_; 138 139 std::unique_ptr delta_performer_; 140 141 // Used by TransferTerminated to figure if this action terminated itself or 142 // was terminated by the action processor. 143 ErrorCode code_; 144 145 // For reporting status to outsiders 146 // 委托代理,用于向外部发送状态 147 DownloadActionDelegate* delegate_; 148 uint64_t bytes_received_{0}; // per file/range 149 uint64_t bytes_received_previous_payloads_{0}; 150 uint64_t bytes_total_{0}; 151 bool download_active_{false}; 152 153 // The file-id for the file we're sharing or the empty string 154 // if we're not using p2p to share. 155 std::string p2p_file_id_; 156 157 // The file descriptor for the p2p file used for caching the payload or -1 158 // if we're not using p2p to share. 159 int p2p_sharing_fd_; 160 161 // Set to |false| if p2p file is not visible. 162 bool p2p_visible_; 163 164 // Loaded from prefs before downloading any payload. 165 size_t resume_payload_index_{0}; 166 167 // Offset of the payload in the download URL, used by UpdateAttempterAndroid. 168 int64_t base_offset_{0}; 169 170 DISALLOW_COPY_AND_ASSIGN(DownloadAction); 171}; 172 173// We want to be sure that we're compiled with large file support on linux, 174// just in case we find ourselves downloading large images. 175static_assert(8 == sizeof(off_t), "off_t not 64 bit"); 176 177} // namespace chromeos_update_engine
1//具体执行Action的方法,老生常谈 2void DownloadAction::PerformAction() { 3 //将downloadAcion作为http_fetcher的委托对象 4 http_fetcher_->set_delegate(this); 5 6 // Get the InstallPlan and read it 7 //获取管道installplanAcion的输入,拿到install_plan 8 CHECK(HasInputObject()); 9 install_plan_ = GetInputObject(); 10 install_plan_.Dump(); 11 //定义接收的字节数 12 bytes_received_ = 0; 13 //定义接收到之前的payloads的字节数 14 bytes_received_previous_payloads_ = 0; 15 //定义接收到的字节总数 16 bytes_total_ = 0; 17 //获取payload的字节总数 18 for (const auto& payload : install_plan_.payloads) 19 bytes_total_ += payload.size; 20 //根据判断条件是否有未完成的升级 21 if (install_plan_.is_resume) { 22 int64_t payload_index = 0; 23 if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) && 24 static_cast(payload_index) < install_plan_.payloads.size()) { 25 // Save the index for the resume payload before downloading any previous 26 // payload, otherwise it will be overwritten. 27 // 在下载以前的payload之前 先保存当前需要恢复payload的标签,否则将会被重写 28 resume_payload_index_ = payload_index; 29 for (int i = 0; i < payload_index; i++) 30 install_plan_.payloads[i].already_applied = true; 31 } 32 } 33 // TODO(senj): check that install plan has at least one payload. 34 // 如果payload不存在,则指定payloads中其中0位置的的成员 35 if (!payload_) 36 payload_ = &install_plan_.payloads[0]; 37 //编译target 分区为不可启动 38 LOG(INFO) << "Marking new slot as unbootable"; 39 if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) { 40 LOG(WARNING) << "Unable to mark new slot " 41 << BootControlInterface::SlotName(install_plan_.target_slot) 42 << ". Proceeding with the update anyway."; 43 } 44 //开始执行download 45 StartDownloading(); 46}
1void DownloadAction::StartDownloading() { 2 //设定download_active_ 标志位为true 3 download_active_ = true; 4 //执行http_fetcher_的清空区间 5 http_fetcher_->ClearRanges(); 6 //判断是否是执行恢复升级,而且payload_是resume_payload_index_ 7 if (install_plan_.is_resume && 8 payload_ == &install_plan_.payloads[resume_payload_index_]) { 9 // Resuming an update so fetch the update manifest metadata first. 10 int64_t manifest_metadata_size = 0; 11 int64_t manifest_signature_size = 0; 12 prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size); 13 prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size); 14 http_fetcher_->AddRange(base_offset_, 15 manifest_metadata_size + manifest_signature_size); 16 // If there're remaining unprocessed data blobs, fetch them. Be careful not 17 // to request data beyond the end of the payload to avoid 416 HTTP response 18 // error codes. 19 int64_t next_data_offset = 0; 20 prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset); 21 uint64_t resume_offset = 22 manifest_metadata_size + manifest_signature_size + next_data_offset; 23 if (!payload_->size) { 24 http_fetcher_->AddRange(base_offset_ + resume_offset); 25 } else if (resume_offset < payload_->size) { 26 http_fetcher_->AddRange(base_offset_ + resume_offset, 27 payload_->size - resume_offset); 28 } 29 } else { 30 if (payload_->size) { 31 http_fetcher_->AddRange(base_offset_, payload_->size); 32 } else { 33 // If no payload size is passed we assume we read until the end of the 34 // stream. 35 http_fetcher_->AddRange(base_offset_); 36 } 37 } 38 //初始化DeltaPerformer类的对象delta_performer_ 39 if (writer_ && writer_ != delta_performer_.get()) { 40 LOG(INFO) << "Using writer for test."; 41 } else { 42 delta_performer_.reset(new DeltaPerformer(prefs_, 43 boot_control_, 44 hardware_, 45 delegate_, 46 &install_plan_, 47 payload_, 48 is_interactive_)); 49 //将delta_performer_赋值给writer_ 50 writer_ = delta_performer_.get(); 51 } 52 //从updateattempterAndroid传入的system_state_为nullptr,所以下面的代码不用看了 53 if (system_state_ != nullptr) { 54 const PayloadStateInterface* payload_state = system_state_->payload_state(); 55 string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size); 56 if (payload_state->GetUsingP2PForSharing()) { 57 // If we're sharing the update, store the file_id to convey 58 // that we should write to the file. 59 p2p_file_id_ = file_id; 60 LOG(INFO) << "p2p file id: " << p2p_file_id_; 61 } else { 62 // Even if we're not sharing the update, it could be that 63 // there's a partial file from a previous attempt with the same 64 // hash. If this is the case, we NEED to clean it up otherwise 65 // we're essentially timing out other peers downloading from us 66 // (since we're never going to complete the file). 67 FilePath path = system_state_->p2p_manager()->FileGetPath(file_id); 68 if (!path.empty()) { 69 if (unlink(path.value().c_str()) != 0) { 70 PLOG(ERROR) << "Error deleting p2p file " << path.value(); 71 } else { 72 LOG(INFO) << "Deleting partial p2p file " << path.value() 73 << " since we're not using p2p to share."; 74 } 75 } 76 } 77 78 // Tweak timeouts on the HTTP fetcher if we're downloading from a 79 // local peer. 80 if (payload_state->GetUsingP2PForDownloading() && 81 payload_state->GetP2PUrl() == install_plan_.download_url) { 82 LOG(INFO) << "Tweaking HTTP fetcher since we're downloading via p2p"; 83 http_fetcher_->set_low_speed_limit(kDownloadP2PLowSpeedLimitBps, 84 kDownloadP2PLowSpeedTimeSeconds); 85 http_fetcher_->set_max_retry_count(kDownloadP2PMaxRetryCount); 86 http_fetcher_->set_connect_timeout(kDownloadP2PConnectTimeoutSeconds); 87 } 88 } 89 //执行数据传输,根据download_action.h文件 http_fetcher_是MultiRangeHttpFetcher指针 90 http_fetcher_->BeginTransfer(install_plan_.download_url); 91}
1//接收字节 2void DownloadAction::ReceivedBytes(HttpFetcher* fetcher, 3 const void* bytes, 4 size_t length) { 5 // Note that bytes_received_ is the current offset. 6 if (!p2p_file_id_.empty()) { 7 WriteToP2PFile(bytes, length, bytes_received_); 8 } 9 10 bytes_received_ += length; 11 uint64_t bytes_downloaded_total = 12 bytes_received_previous_payloads_ + bytes_received_; 13 //通过委托delegate_向外发送通知,整理其实就是updateAttempterAndroid的BytesReceived方法 14 if (delegate_ && download_active_) { 15 delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_); 16 } 17 //调用DeltaPerformer的Write方法执行具体的写入数据的操作 18 if (writer_ && !writer_->Write(bytes, length, &code_)) { 19 if (code_ != ErrorCode::kSuccess) { 20 LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_ 21 << ") in DeltaPerformer's Write method when " 22 << "processing the received payload -- Terminating processing"; 23 } 24 // Delete p2p file, if applicable. 25 if (!p2p_file_id_.empty()) 26 CloseP2PSharingFd(true); 27 // Don't tell the action processor that the action is complete until we get 28 // the TransferTerminated callback. Otherwise, this and the HTTP fetcher 29 // objects may get destroyed before all callbacks are complete. 30 TerminateProcessing(); 31 return; 32 } 33 34 // Call p2p_manager_->FileMakeVisible() when we've successfully 35 // verified the manifest! 36 if (!p2p_visible_ && system_state_ && delta_performer_.get() && 37 delta_performer_->IsManifestValid()) { 38 LOG(INFO) << "Manifest has been validated. Making p2p file visible."; 39 system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_); 40 p2p_visible_ = true; 41 } 42}
1//传输完成方法 2void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) { 3 //如果writer_是打开状态,先关闭 4 if (writer_) { 5 LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer."; 6 if (delta_performer_.get() == writer_) { 7 // no delta_performer_ in tests, so leave the test writer in place 8 writer_ = nullptr; 9 } 10 } 11 //下载结束了,设置download_active为false 12 download_active_ = false; 13 //拿到升级状态是成功还是数据传输失败 14 ErrorCode code = 15 successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError; 16 //根据升级状态,如果是成功,执行校验VerifyPayload校验下载的payload的hash和size 17 if (code == ErrorCode::kSuccess) { 18 if (delta_performer_ && !payload_->already_applied) 19 code = delta_performer_->VerifyPayload(payload_->hash, payload_->size); 20 if (code == ErrorCode::kSuccess) { 21 if (payload_ < &install_plan_.payloads.back() && 22 system_state_->payload_state()->NextPayload()) { 23 LOG(INFO) << "Incrementing to next payload"; 24 // No need to reset if this payload was already applied. 25 if (delta_performer_ && !payload_->already_applied) 26 DeltaPerformer::ResetUpdateProgress(prefs_, false); 27 // Start downloading next payload. 28 bytes_received_previous_payloads_ += payload_->size; 29 payload_++; 30 install_plan_.download_url = 31 system_state_->payload_state()->GetCurrentUrl(); 32 StartDownloading(); 33 return; 34 } 35 // Log UpdateEngine.DownloadAction.* histograms to help diagnose 36 // long-blocking oeprations. 37 std::string histogram_output; 38 base::StatisticsRecorder::WriteGraph( 39 "UpdateEngine.DownloadAction.", &histogram_output); 40 LOG(INFO) << histogram_output; 41 } else { 42 LOG(ERROR) << "Download of " << install_plan_.download_url 43 << " failed due to payload verification error."; 44 // Delete p2p file, if applicable. 45 if (!p2p_file_id_.empty()) 46 CloseP2PSharingFd(true); 47 } 48 } 49 50 // Write the path to the output pipe if we're successful. 51 // 将install_plan设置为SetOutputObject 52 if (code == ErrorCode::kSuccess && HasOutputPipe()) 53 SetOutputObject(install_plan_); 54 // 执行actionprocessor的actioncomplete 55 processor_->ActionComplete(this, code); 56}
1//传输终止的方法 2void DownloadAction::TransferTerminated(HttpFetcher* fetcher) { 3 //如果没成功 4 if (code_ != ErrorCode::kSuccess) { 5 //执行actionprocessor的actioncomplete 6 processor_->ActionComplete(this, code_); 7 } else if (payload_->already_applied) { 8 LOG(INFO) << "TransferTerminated with ErrorCode::kSuccess when the current " 9 "payload has already applied, treating as TransferComplete."; 10 TransferComplete(fetcher, true); 11 } 12}
1//可以看出来,DownloadAcion的暂停,恢复,停止处理方法中都是执行对应http_fetcher的方法 2//暂停action 3void DownloadAction::SuspendAction() { 4 http_fetcher_->Pause(); 5} 6//恢复action 7void DownloadAction::ResumeAction() { 8 http_fetcher_->Unpause(); 9} 10//停止处理action 11void DownloadAction::TerminateProcessing() { 12 if (writer_) { 13 writer_->Close(); 14 writer_ = nullptr; 15 } 16 download_active_ = false; 17 CloseP2PSharingFd(false); // Keep p2p file. 18 // Terminates the transfer. The action is terminated, if necessary, when the 19 // TransferTerminated callback is received. 20 http_fetcher_->TerminateTransfer(); 21}
从整理代码来看,其中最重要的两个模块http_fetcher_,和 writer_,这两个模块一个进行数据传输,一个进行写入,这两个我准备单独进行讲解,属于升级过程中最重要的部分,目前来说,我只想先串联起这四个action
1// This action will hash all the partitions of the target slot involved in the 2// update. The hashes are then verified against the ones in the InstallPlan. 3// If the target hash does not match, the action will fail. In case of failure, 4// the error code will depend on whether the source slot hashes are provided and 5// match. 6// 这个action将取升级包中所有目标slot的分区的hash值,与install_pan_中存放的进行校验对比 7// 如果目标hash不匹配,那么这个action执行失败,如果失败了,error code将取决于源slot的hash值 8// 是否提供而且匹配 9namespace chromeos_update_engine { 10 11// The step FilesystemVerifier is on. On kVerifyTargetHash it computes the hash 12// on the target partitions based on the already populated size and verifies it 13// matches the one in the target_hash in the InstallPlan. 14// If the hash matches, then we skip the kVerifySourceHash step, otherwise we 15// need to check if the source is the root cause of the mismatch. 16// FilesystemVerifier步骤已打开。 在kVerifyTargetHash上,它根据已填充的大小计算目标分区上的哈希, 17// 并验证它是否与InstallPlan中target_hash中的哈希匹配。 18// 如果哈希匹配,那么我们跳过kVerifySourceHash步骤,否则我们需要检查源是否是不匹配的根本原因。 19// 定义枚举,校验升级分区hash 和 校验基础版本的hash 20enum class VerifierStep { 21 kVerifyTargetHash, 22 kVerifySourceHash, 23}; 24 25class FilesystemVerifierAction : public InstallPlanAction { 26 public: 27 FilesystemVerifierAction() = default; 28 29 void PerformAction() override; 30 void TerminateProcessing() override; 31 32 //test部分 不关注 33 // Used for testing. Return true if Cleanup() has not yet been called due 34 // to a callback upon the completion or cancellation of the verifier action. 35 // A test should wait until IsCleanupPending() returns false before 36 // terminating the main loop. 37 bool IsCleanupPending() const; 38 39 // Debugging/logging 40 static std::string StaticType() { return "FilesystemVerifierAction"; } 41 std::string Type() const override { return StaticType(); } 42 43 private: 44 // Starts the hashing of the current partition. If there aren't any partitions 45 // remaining to be hashed, it finishes the action. 46 // 开始计算hash 47 void StartPartitionHashing(); 48 49 // Schedules the asynchronous read of the filesystem. 50 // 异步读取文件系统的信息,应该是用于system 和 vendor 51 void ScheduleRead(); 52 53 // Called from the main loop when a single read from |src_stream_| succeeds or 54 // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively. 55 // 对于读取结果的回调方法 56 void OnReadDoneCallback(size_t bytes_read); 57 void OnReadErrorCallback(const brillo::Error* error); 58 59 // When the read is done, finalize the hash checking of the current partition 60 // and continue checking the next one. 61 // 当读取当前的分区已经接收,继续读取下一个 62 void FinishPartitionHashing(); 63 64 // Cleans up all the variables we use for async operations and tells the 65 // ActionProcessor we're done w/ |code| as passed in. |cancelled_| should be 66 // true if TerminateProcessing() was called. 67 void Cleanup(ErrorCode code); 68 69 // The type of the partition that we are verifying. 70 VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash; 71 72 // The index in the install_plan_.partitions vector of the partition currently 73 // being hashed. 74 // 该标志位是install_plan_.partitions 容器中当前计算好hash的位置 75 size_t partition_index_{0}; 76 77 // If not null, the FileStream used to read from the device. 78 // 定义stream指针,使用FileStream从设备中读取分区信息 79 brillo::StreamPtr src_stream_; 80 81 // Buffer for storing data we read. 82 // 用于存储我们读取的数据的缓冲区。 83 brillo::Blob buffer_; 84 85 //定义两个参数,是否读取完了,是否取消 86 bool read_done_{false}; // true if reached EOF on the input stream. 87 bool cancelled_{false}; // true if the action has been cancelled. 88 89 // The install plan we're passed in via the input pipe. 90 InstallPlan install_plan_; 91 92 // Calculates the hash of the data. 93 // HashCalculator用于计算分区的hash 94 std::unique_ptrhasher_; 95 96 // Reads and hashes this many bytes from the head of the input stream. This 97 // field is initialized from the corresponding InstallPlan::Partition size, 98 // when the partition starts to be hashed. 99 int64_t remaining_size_{0}; 100 101 DISALLOW_COPY_AND_ASSIGN(FilesystemVerifierAction); 102};
1//具体执行action 2void FilesystemVerifierAction::PerformAction() { 3 // Will tell the ActionProcessor we've failed if we return. 4 ScopedActionCompleter abort_action_completer(processor_, this); 5 6 if (!HasInputObject()) { 7 LOG(ERROR) << "FilesystemVerifierAction missing input object."; 8 return; 9 } 10 //获取上一个action的输入,即DownloadAcion的输入 11 install_plan_ = GetInputObject(); 12 //如果partitions是空的,说明不需要校验直接执行将install_plan_设置为HasOutputPipe 13 //给下一个action 14 if (install_plan_.partitions.empty()) { 15 LOG(INFO) << "No partitions to verify."; 16 if (HasOutputPipe()) 17 SetOutputObject(install_plan_); 18 abort_action_completer.set_code(ErrorCode::kSuccess); 19 return; 20 } 21 //开始执行计算hash 22 StartPartitionHashing(); 23 abort_action_completer.set_should_complete(false); 24}
1//开始计算hash 2void FilesystemVerifierAction::StartPartitionHashing() { 3 //如果partition_index_等于partitions的size,也就意味着已经处理完所有分区了 4 //首先第一个进来的partition_index_ 是0,所以不满足 5 if (partition_index_ == install_plan_.partitions.size()) { 6 Cleanup(ErrorCode::kSuccess); 7 return; 8 } 9 //取出0位置上的分区,从这里就可以看出来,StartPartitionHashing这个方法不止会走一次 10 //处理完一个分区后,会继续执行该方法,处理第二个分区 11 InstallPlan::Partition& partition = 12 install_plan_.partitions[partition_index_]; 13 //定义分区路径 14 string part_path; 15 //选择校验步骤,根据.h文件中的定义 16 //VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash;这里使用的是target 17 switch (verifier_step_) { 18 case VerifierStep::kVerifySourceHash: 19 part_path = partition.source_path; 20 remaining_size_ = partition.source_size; 21 break; 22 case VerifierStep::kVerifyTargetHash: 23 //定义获取当前分区的路径和大小 24 part_path = partition.target_path; 25 remaining_size_ = partition.target_size; 26 break; 27 } 28 LOG(INFO) << "Hashing partition " << partition_index_ << " (" 29 << partition.name << ") on device " << part_path; 30 //如果当前分区的路劲是空的 执行清除处理 31 if (part_path.empty()) 32 return Cleanup(ErrorCode::kFilesystemVerifierError); 33 34 brillo::ErrorPtr error; 35 //根据分区路径,使用FileStream读取分区信息 36 src_stream_ = brillo::FileStream::Open( 37 base::FilePath(part_path), 38 brillo::Stream::AccessMode::READ, 39 brillo::FileStream::Disposition::OPEN_EXISTING, 40 &error); 41 42 if (!src_stream_) { 43 LOG(ERROR) << "Unable to open " << part_path << " for reading"; 44 return Cleanup(ErrorCode::kFilesystemVerifierError); 45 } 46 //执行每次存储的数据的大小,也就是每次校验数据的hash的大小 47 buffer_.resize(kReadFileBufferSize); 48 //设定read_done标志位为 false 49 read_done_ = false; 50 //初始化哈希计算器 用于计算需要比对的数据 51 hasher_.reset(new HashCalculator()); 52 53 // Start the first read.开始首次读取, 54 // 需要读取的分区找好了,大小定义了,hash计算器也起来了,开始工作了 55 ScheduleRead(); 56}
1//异步读取文件系统的信息 2void FilesystemVerifierAction::ScheduleRead() { 3 //定义读取的字节大小取buffer_和remaining_size_的最小值 4 //分区的大小最后的部分可能还不够buffer_的大小 5 size_t bytes_to_read = std::min(static_cast(buffer_.size()), 6 remaining_size_); 7 //如果 bytes_to_read为0,那说明读取完了,执行回调函数 8 if (!bytes_to_read) { 9 OnReadDoneCallback(0); 10 return; 11 } 12 //使用FileStream 的指针对象src_stream异步读取数据 13 //绑定读取完成和读取失败两个回调函数 14 //buffer.data() 定义缓冲区的大小 bytes_to_read为指定读取的字节数 15 bool read_async_ok = src_stream_->ReadAsync( 16 buffer_.data(), 17 bytes_to_read, 18 base::Bind(&FilesystemVerifierAction::OnReadDoneCallback, 19 base::Unretained(this)), 20 base::Bind(&FilesystemVerifierAction::OnReadErrorCallback, 21 base::Unretained(this)), 22 nullptr); 23 24 //如果执行失败,那就执行清除工作 25 if (!read_async_ok) { 26 LOG(ERROR) << "Unable to schedule an asynchronous read from the stream."; 27 Cleanup(ErrorCode::kError); 28 } 29}
1//读取成功的回调函数 2void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) { 3 //如果传入的读取字节数为0,那说明读取完成了,设定标志位read_done为true 4 if (bytes_read == 0) { 5 read_done_ = true; 6 //读取到的不为0,那就需要继续读取了 7 } else { 8 //将剩余的大小减去当前读取的大小,更新当前的余量 9 remaining_size_ -= bytes_read; 10 CHECK(!read_done_); 11 //执行hasher_计算出当前的读取大小的hash值 12 if (!hasher_->Update(buffer_.data(), bytes_read)) { 13 LOG(ERROR) << "Unable to update the hash."; 14 //如果计算的时候失败了,那对不起,清除现在的操作 15 Cleanup(ErrorCode::kError); 16 return; 17 } 18 } 19 20 // We either terminate the current partition or have more data to read. 21 // 我们要么终止当前分区,或者有更多数据去读取 22 if (cancelled_) 23 return Cleanup(ErrorCode::kError); 24 //如果已经读取完成,获取需要读取剩余大小为0,说明已经读取完了 25 if (read_done_ || remaining_size_ == 0) { 26 //如果是read_done=true,但是还有余量,那就是出错了,执行cleanup操作 27 if (remaining_size_ != 0) { 28 LOG(ERROR) << "Failed to read the remaining " << remaining_size_ 29 << " bytes from partition " 30 << install_plan_.partitions[partition_index_].name; 31 return Cleanup(ErrorCode::kFilesystemVerifierError); 32 } 33 //执行结束hash的计算 34 return FinishPartitionHashing(); 35 } 36 //除了以上的情况,那就是没读完,继续循环读取,自己调自己,知道完成可以退出 37 ScheduleRead(); 38} 39 40//读取失败的回调函数 41void FilesystemVerifierAction::OnReadErrorCallback( 42 const brillo::Error* error) { 43 // TODO(deymo): Transform the read-error into an specific ErrorCode. 44 LOG(ERROR) << "Asynchronous read failed."; 45 //那没啥说的,执行清除的操作 46 Cleanup(ErrorCode::kError); 47}
1//结束分区hash计算了,那开始比对吧 2void FilesystemVerifierAction::FinishPartitionHashing() { 3 //相当于汇总计算出的hash,没法汇总,清除 4 if (!hasher_->Finalize()) { 5 LOG(ERROR) << "Unable to finalize the hash."; 6 return Cleanup(ErrorCode::kError); 7 } 8 //拿到当前的partition 9 InstallPlan::Partition& partition = 10 install_plan_.partitions[partition_index_]; 11 LOG(INFO) << "Hash of " << partition.name << ": " 12 << Base64Encode(hasher_->raw_hash()); 13 //根据校验步骤,进行判断 14 switch (verifier_step_) { 15 case VerifierStep::kVerifyTargetHash: 16 //如果target_hash与当前读取 到的不一致,那就是校验失败了, 17 //这里的target_hash其实就是升级包里的信息 18 if (partition.target_hash != hasher_->raw_hash()) { 19 LOG(ERROR) << "New '" << partition.name 20 << "' partition verification failed."; 21 //校验target失败了,好吧,改为校验source_hash 22 //如果source_hash为空,那说明是整包,执行清除 23 if (partition.source_hash.empty()) { 24 // No need to verify source if it is a full payload. 25 return Cleanup(ErrorCode::kNewRootfsVerificationError); 26 } 27 // If we have not verified source partition yet, now that the target 28 // partition does not match, and it's not a full payload, we need to 29 // switch to kVerifySourceHash step to check if it's because the source 30 // partition does not match either. 31 // 如果我们还没有校验源分区,现在目标分区由不匹配,而且又不是整包 32 // 那就选择kVerifySourceHash去检查看看是不是因为基础版本的hash不一致导致的 33 verifier_step_ = VerifierStep::kVerifySourceHash; 34 } else { 35 //如果校验成功了,将partition_index_加1,校验下一个分区的hash 36 partition_index_++; 37 } 38 break; 39 //这种情况说明升级后的分区已经校验失败了 40 case VerifierStep::kVerifySourceHash: 41 //如果基础的hash值也没校验过,那就是system不匹配了,先打印了一堆log,然后执行清理 42 if (partition.source_hash != hasher_->raw_hash()) { 43 LOG(ERROR) << "Old '" << partition.name 44 << "' partition verification failed."; 45 LOG(ERROR) << "This is a server-side error due to mismatched delta" 46 << " update image!"; 47 LOG(ERROR) << "The delta I've been given contains a " << partition.name 48 << " delta update that must be applied over a " 49 << partition.name << " with a specific checksum, but the " 50 << partition.name 51 << " we're starting with doesn't have that checksum! This" 52 " means that the delta I've been given doesn't match my" 53 " existing system. The " 54 << partition.name << " partition I have has hash: " 55 << Base64Encode(hasher_->raw_hash()) 56 << " but the update expected me to have " 57 << Base64Encode(partition.source_hash) << " ."; 58 LOG(INFO) << "To get the checksum of the " << partition.name 59 << " partition run this command: dd if=" 60 << partition.source_path 61 << " bs=1M count=" << partition.source_size 62 << " iflag=count_bytes 2>/dev/null | openssl dgst -sha256 " 63 "-binary | openssl base64"; 64 LOG(INFO) << "To get the checksum of partitions in a bin file, " 65 << "run: .../src/scripts/sha256_partitions.sh .../file.bin"; 66 return Cleanup(ErrorCode::kDownloadStateInitializationError); 67 } 68 // The action will skip kVerifySourceHash step if target partition hash 69 // matches, if we are in this step, it means target hash does not match, 70 // and now that the source partition hash matches, we should set the error 71 // code to reflect the error in target partition. 72 // We only need to verify the source partition which the target hash does 73 // not match, the rest of the partitions don't matter. 74 // 目前这个情况,是升级后的版本没校验过去,而基础版本还校验过去了,单纯的升级失败, 75 // 反馈目标版本升级失败的错误类型 76 return Cleanup(ErrorCode::kNewRootfsVerificationError); 77 } 78 // Start hashing the next partition, if any.开始执行校验下一个hash 79 // 重置hasher_ 80 hasher_.reset(); 81 //清除缓存区 82 buffer_.clear(); 83 //关闭FileStream流 84 src_stream_->CloseBlocking(nullptr); 85 //partition_index已经加1了,计算install_plan_.partitions 下一个分区的hash 86 StartPartitionHashing(); 87}
1//停止当前action 2void FilesystemVerifierAction::TerminateProcessing() { 3 cancelled_ = true; 4 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true. 5} 6 7bool FilesystemVerifierAction::IsCleanupPending() const { 8 return src_stream_ != nullptr; 9} 10 11void FilesystemVerifierAction::Cleanup(ErrorCode code) { 12 //重置src_stream 13 src_stream_.reset(); 14 // This memory is not used anymore. 15 // 清空 buffer 16 buffer_.clear(); 17 18 if (cancelled_) 19 return; 20 //判断状态,将install_plan_设置为SetOutputObject 21 if (code == ErrorCode::kSuccess && HasOutputPipe()) 22 SetOutputObject(install_plan_); 23 //执行actionprocessor的actioncomplete 24 processor_->ActionComplete(this, code); 25}
可以看出filesystemverifieraction大致是这么个流程,挨个校验需要升级的分区的hash值,先读取当前分区的信息,定义缓冲区,计算缓冲区的hash,知道分区中所有的数据hash计算完成了,拿到整个分区的hash,与升级包中存放的升级后的分区hash值做比对,如果没问题,那就继续,如果不行,再校验基础版本的,看是基础版本的问题,还是单纯的升级失败了,
1// The Postinstall Runner Action is responsible for running the postinstall 2// script of a successfully downloaded update. 3// Postinstall Runner Action负责运行安装后成功下载的更新的脚本。 4namespace chromeos_update_engine { 5 6class BootControlInterface; 7 8class PostinstallRunnerAction : public InstallPlanAction { 9 public: 10 PostinstallRunnerAction(BootControlInterface* boot_control, 11 HardwareInterface* hardware) 12 : boot_control_(boot_control), hardware_(hardware) {} 13 14 // InstallPlanAction overrides.重写父类action的方法 15 void PerformAction() override; 16 void SuspendAction() override; 17 void ResumeAction() override; 18 void TerminateProcessing() override; 19 20 class DelegateInterface { 21 public: 22 virtual ~DelegateInterface() = default; 23 24 // Called whenever there is an overall progress update from the postinstall 25 // programs.在安装后程序进行总体进度更新时调用。 26 virtual void ProgressUpdate(double progress) = 0; 27 }; 28 29 void set_delegate(DelegateInterface* delegate) { delegate_ = delegate; } 30 31 // Debugging/logging 32 static std::string StaticType() { return "PostinstallRunnerAction"; } 33 std::string Type() const override { return StaticType(); } 34 35 private: 36 friend class PostinstallRunnerActionTest; 37 FRIEND_TEST(PostinstallRunnerActionTest, ProcessProgressLineTest); 38 39 void PerformPartitionPostinstall(); 40 41 // Called whenever the |progress_fd_| has data available to read. 42 // 当progress_fd 有数据读取的时候调用 43 void OnProgressFdReady(); 44 45 // Updates the action progress according to the |line| passed from the 46 // postinstall program. Valid lines are: 47 // global_progress48 // should be between 0.0 and 1.0; sets the progress to the 49 // value. 50 bool ProcessProgressLine(const std::string& line); 51 52 // Report the progress to the delegate given that the postinstall operation 53 // for |current_partition_| has a current progress of |frac|, a value between 54 // 0 and 1 for that step. 55 // 在进行postinstall操作后,将进度报告给委托代理 56 // 用于| current_partition_ | 当前进度为| frac |,其值介于该步骤的0和1。 57 void ReportProgress(double frac); 58 59 // Cleanup the setup made when running postinstall for a given partition. 60 // Unmount and remove the mountpoint directory if needed and cleanup the 61 // status file descriptor and message loop task watching for it. 62 void Cleanup(); 63 64 // Subprocess::Exec callback. 65 void CompletePartitionPostinstall(int return_code, 66 const std::string& output); 67 68 // Complete the Action with the passed |error_code| and mark the new slot as 69 // ready. Called when the post-install script was run for all the partitions. 70 void CompletePostinstall(ErrorCode error_code); 71 72 InstallPlan install_plan_; 73 74 // The path where the filesystem will be mounted during post-install. 75 std::string fs_mount_dir_; 76 77 // The partition being processed on the list of partitions specified in the 78 // InstallPlan.正在执行处理的分区 79 size_t current_partition_{0}; 80 81 // A non-negative value representing the estimated weight of each partition 82 // passed in the install plan. The weight is used to predict the overall 83 // progress from the individual progress of each partition and should 84 // correspond to the time it takes to run it. 85 // 一个非负值,表示在安装计划中通过的每个分区的估计重量。 86 // 权重用于根据每个分区的单独进度来预测总体进度,并且应与运行该分区所花费的时间相对应。 87 std::vector partition_weight_; 88 89 // The sum of all the weights in |partition_weight_|. 90 // partition_weight_所有权重的总和 91 double total_weight_{0}; 92 93 // The sum of all the weights in |partition_weight_| up to but not including 94 // the |current_partition_|. 95 // 所有partition_weight_中所有权重的总和,但是不包含当前分区 96 double accumulated_weight_{0}; 97 98 // The delegate used to notify of progress updates, if any. 99 DelegateInterface* delegate_{nullptr}; 100 101 // The BootControlInerface used to mark the new slot as ready. 102 BootControlInterface* boot_control_; 103 104 // HardwareInterface used to signal powerwash. 105 HardwareInterface* hardware_; 106 107 // Whether the Powerwash was scheduled before invoking post-install script. 108 // Used for cleaning up if post-install fails. 109 bool powerwash_scheduled_{false}; 110 111 // Postinstall command currently running, or 0 if no program running. 112 pid_t current_command_{0}; 113 114 // True if |current_command_| has been suspended by SuspendAction(). 115 bool is_current_command_suspended_{false}; 116 117 // The parent progress file descriptor used to watch for progress reports from 118 // the postinstall program and the task watching for them. 119 // 父进度文件描述符用于监视来自安装后程序的进度报告以及用于监视它们的任务。 120 int progress_fd_{-1}; 121 brillo::MessageLoop::TaskId progress_task_{brillo::MessageLoop::kTaskIdNull}; 122 123 // A buffer of a partial read line from the progress file descriptor. 124 // 来自进度文件描述符的部分读取行的缓冲区。 125 std::string progress_buffer_; 126 127 DISALLOW_COPY_AND_ASSIGN(PostinstallRunnerAction); 128};
1using brillo::MessageLoop; 2using std::string; 3using std::vector; 4 5void PostinstallRunnerAction::PerformAction() { 6 //获取filesystem_verifier_action操作之后的install_plan_ 7 CHECK(HasInputObject()); 8 install_plan_ = GetInputObject(); 9 10 if (install_plan_.powerwash_required) { 11 if (hardware_->SchedulePowerwash()) { 12 powerwash_scheduled_ = true; 13 } else { 14 return CompletePostinstall(ErrorCode::kPostinstallPowerwashError); 15 } 16 } 17 18 // Initialize all the partition weights.初始化所有分区权重 19 // 现在相当于partition_weight_的大小就是分区的个数 20 partition_weight_.resize(install_plan_.partitions.size()); 21 // 定义权重总和初始值为0 22 total_weight_ = 0; 23 //将所有分区的run_postinstall参数放入partition_weight对应位置上 24 for (size_t i = 0; i < install_plan_.partitions.size(); ++i) { 25 // TODO(deymo): This code sets the weight to all the postinstall commands, 26 // but we could remember how long they took in the past and use those 27 // values. 28 partition_weight_[i] = install_plan_.partitions[i].run_postinstall; 29 //将partition_weight每个位置上的元素放入total_weight_数组中 30 total_weight_ += partition_weight_[i]; 31 } 32 //定义accumulated_weight_初始值为0 33 accumulated_weight_ = 0; 34 //报告进度 35 ReportProgress(0); 36 //执行分区postinstall 37 PerformPartitionPostinstall(); 38}
1 //执行分区postinstall 2void PostinstallRunnerAction::PerformPartitionPostinstall() { 3 //判断是否有run_post_install参数,如果没有直接return成功 4 if (!install_plan_.run_post_install) { 5 LOG(INFO) << "Skipping post-install according to install plan."; 6 return CompletePostinstall(ErrorCode::kSuccess); 7 } 8 //判断url是否为空,如果为空,执行完成postinstall 9 if (install_plan_.download_url.empty()) { 10 LOG(INFO) << "Skipping post-install during rollback"; 11 return CompletePostinstall(ErrorCode::kSuccess); 12 } 13 14 // Skip all the partitions that don't have a post-install step. 15 // 如果当前分区不包含run_postinstall,则确认下个分区 16 while (current_partition_ < install_plan_.partitions.size() && 17 !install_plan_.partitions[current_partition_].run_postinstall) { 18 VLOG(1) << "Skipping post-install on partition " 19 << install_plan_.partitions[current_partition_].name; 20 current_partition_++; 21 } 22 //如果当前分区的大小等于partitions的大小,说明已经执行完了,当前的是最后一个 23 if (current_partition_ == install_plan_.partitions.size()) 24 return CompletePostinstall(ErrorCode::kSuccess); 25 //根据current_partition_拿到当前分区 26 const InstallPlan::Partition& partition = 27 install_plan_.partitions[current_partition_]; 28 //获取需要处理分区的名称 29 const string mountable_device = 30 utils::MakePartitionNameForMount(partition.target_path); 31 //如果没获取到,返回错误 32 if (mountable_device.empty()) { 33 LOG(ERROR) << "Cannot make mountable device from " << partition.target_path; 34 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 35 } 36 37 // Perform post-install for the current_partition_ partition. At this point we 38 // need to call CompletePartitionPostinstall to complete the operation and 39 // cleanup. 40 // 对当前分区执行post-install,需要调用CompletePartitionPostinstall并执行清理 41#ifdef __ANDROID__ 42 //执行文件路径为postinstall 43 fs_mount_dir_ = "/postinstall"; 44#else // __ANDROID__ 45 base::FilePath temp_dir; 46 TEST_AND_RETURN(base::CreateNewTempDirectory("au_postint_mount", &temp_dir)); 47 fs_mount_dir_ = temp_dir.value(); 48#endif // __ANDROID__ 49 50 // Double check that the fs_mount_dir is not busy with a previous mounted 51 // filesystem from a previous crashed postinstall step. 52 // 仔细检查fs_mount_dir是否不忙于先前崩溃的安装后步骤中的先前安装的文件系统。 53 // 如果有,执行unmountFileSystem方法 54 if (utils::IsMountpoint(fs_mount_dir_)) { 55 LOG(INFO) << "Found previously mounted filesystem at " << fs_mount_dir_; 56 utils::UnmountFilesystem(fs_mount_dir_); 57 } 58 //检查是否是绝对路径 59 base::FilePath postinstall_path(partition.postinstall_path); 60 if (postinstall_path.IsAbsolute()) { 61 LOG(ERROR) << "Invalid absolute path passed to postinstall, use a relative" 62 "path instead: " 63 << partition.postinstall_path; 64 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 65 } 66 //检查abs_path是否以"/postinstall"开头 67 string abs_path = 68 base::FilePath(fs_mount_dir_).Append(postinstall_path).value(); 69 if (!base::StartsWith( 70 abs_path, fs_mount_dir_, base::CompareCase::SENSITIVE)) { 71 LOG(ERROR) << "Invalid relative postinstall path: " 72 << partition.postinstall_path; 73 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 74 } 75 76#ifdef __ANDROID__ 77 // In Chromium OS, the postinstall step is allowed to write to the block 78 // device on the target image, so we don't mark it as read-only and should 79 // be read-write since we just wrote to it during the update. 80 81 // Mark the block device as read-only before mounting for post-install. 82 // 在挂载用于post-install之前,标记分区为只读 83 if (!utils::SetBlockDeviceReadOnly(mountable_device, true)) { 84 return CompletePartitionPostinstall( 85 1, "Error marking the device " + mountable_device + " read only."); 86 } 87#endif // __ANDROID__ 88 //以只读方式挂载将执行postinstall的分区 89 if (!utils::MountFilesystem(mountable_device, 90 fs_mount_dir_, 91 MS_RDONLY, 92 partition.filesystem_type, 93 constants::kPostinstallMountOptions)) { 94 return CompletePartitionPostinstall( 95 1, "Error mounting the device " + mountable_device); 96 } 97 98 LOG(INFO) << "Performing postinst (" << partition.postinstall_path << " at " 99 << abs_path << ") installed on device " << partition.target_path 100 << " and mountable device " << mountable_device; 101 102 // Logs the file format of the postinstall script we are about to run. This 103 // will help debug when the postinstall script doesn't match the architecture 104 // of our build. 105 LOG(INFO) << "Format file for new " << partition.postinstall_path 106 << " is: " << utils::GetFileFormat(abs_path); 107 108 // Runs the postinstall script asynchronously to free up the main loop while 109 // it's running.异步执行postinstall,构造command 110 vectorcommand = {abs_path}; 111#ifdef __ANDROID__ 112 // In Brillo and Android, we pass the slot number and status fd. 113 // 放入两个需要的参数,target_slot,kPostinstallStatusFd 114 command.push_back(std::to_string(install_plan_.target_slot)); 115 command.push_back(std::to_string(kPostinstallStatusFd)); 116#else 117 // Chrome OS postinstall expects the target rootfs as the first parameter. 118 command.push_back(partition.target_path); 119#endif // __ANDROID__ 120 121 //执行command脚本,并绑定CompletePartitionPostinstall 122 current_command_ = Subprocess::Get().ExecFlags( 123 command, 124 Subprocess::kRedirectStderrToStdout, 125 {kPostinstallStatusFd}, 126 base::Bind(&PostinstallRunnerAction::CompletePartitionPostinstall, 127 base::Unretained(this))); 128 // Subprocess::Exec should never return a negative process id. 129 CHECK_GE(current_command_, 0); 130 131 //判断command_是不是已经运行起来了 132 if (!current_command_) { 133 CompletePartitionPostinstall(1, "Postinstall didn't launch"); 134 return; 135 } 136 137 // Monitor the status file descriptor. 138 //监视文件描述符状态。看到这里,我不仅想说,都是什么玩意儿 139 //其实就是看看这个文件能不能正常打开执行 140 progress_fd_ = 141 Subprocess::Get().GetPipeFd(current_command_, kPostinstallStatusFd); 142 int fd_flags = fcntl(progress_fd_, F_GETFL, 0) | O_NONBLOCK; 143 if (HANDLE_EINTR(fcntl(progress_fd_, F_SETFL, fd_flags)) < 0) { 144 PLOG(ERROR) << "Unable to set non-blocking I/O mode on fd " << progress_fd_; 145 } 146 progress_task_ = MessageLoop::current()->WatchFileDescriptor( 147 FROM_HERE, 148 progress_fd_, 149 MessageLoop::WatchMode::kWatchRead, 150 true, 151 base::Bind(&PostinstallRunnerAction::OnProgressFdReady, 152 base::Unretained(this))); 153}
1void PostinstallRunnerAction::OnProgressFdReady() { 2 //定义buf 3 char buf[1024]; 4 //定义读取字节数 5 size_t bytes_read; 6 //根据bytes_read定义while循环 7 do { 8 //定义初始读取为0 9 bytes_read = 0; 10 bool eof; 11 bool ok = 12 utils::ReadAll(progress_fd_, buf, arraysize(buf), &bytes_read, &eof); 13 progress_buffer_.append(buf, bytes_read); 14 // Process every line. 15 vectorlines = base::SplitString( 16 progress_buffer_, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); 17 if (!lines.empty()) { 18 progress_buffer_ = lines.back(); 19 lines.pop_back(); 20 for (const auto& line : lines) { 21 ProcessProgressLine(line); 22 } 23 } 24 if (!ok || eof) { 25 // There was either an error or an EOF condition, so we are done watching 26 // the file descriptor. 27 MessageLoop::current()->CancelTask(progress_task_); 28 progress_task_ = MessageLoop::kTaskIdNull; 29 return; 30 } 31 } while (bytes_read); 32} 33 34bool PostinstallRunnerAction::ProcessProgressLine(const string& line) { 35 double frac = 0; 36 if (sscanf(line.c_str(), "global_progress %lf", &frac) == 1 && 37 !std::isnan(frac)) { 38 ReportProgress(frac); 39 return true; 40 } 41 42 return false; 43}
1//报告进度 2void PostinstallRunnerAction::ReportProgress(double frac) { 3 //如果委托对象不存在,直接返回 4 if (!delegate_) 5 return; 6 //如果当前分区大于等于分区权重的大小,所以已经最后一个分区了 7 if (current_partition_ >= partition_weight_.size()) { 8 //执行委托对象的ProgressUpdate,传入参数1.0,也就是updateAttempterAndroid的方法 9 delegate_->ProgressUpdate(1.); 10 return; 11 } 12 if (!std::isfinite(frac) || frac < 0) 13 frac = 0; 14 if (frac > 1) 15 frac = 1; 16 //计算出postinstallaction的进度 17 double postinst_action_progress = 18 (accumulated_weight_ + partition_weight_[current_partition_] * frac) / 19 total_weight_; 20 //执行委托delegate_的ProgressUpdate,向外发送进度 21 delegate_->ProgressUpdate(postinst_action_progress); 22} 23 24void PostinstallRunnerAction::Cleanup() { 25 utils::UnmountFilesystem(fs_mount_dir_); 26#ifndef __ANDROID__ 27 if (!base::DeleteFile(base::FilePath(fs_mount_dir_), false)) { 28 PLOG(WARNING) << "Not removing temporary mountpoint " << fs_mount_dir_; 29 } 30#endif // !__ANDROID__ 31 fs_mount_dir_.clear(); 32 33 progress_fd_ = -1; 34 if (progress_task_ != MessageLoop::kTaskIdNull) { 35 MessageLoop::current()->CancelTask(progress_task_); 36 progress_task_ = MessageLoop::kTaskIdNull; 37 } 38 progress_buffer_.clear(); 39}
1//成功处理完单个分区的postinstall 2void PostinstallRunnerAction::CompletePartitionPostinstall( 3 int return_code, const string& output) { 4 //清除当前command 也就是清除当前分区的执行脚本 5 current_command_ = 0; 6 //执行清除 7 Cleanup(); 8 //根据传入的参数,定义不同的error类型 9 if (return_code != 0) { 10 LOG(ERROR) << "Postinst command failed with code: " << return_code; 11 ErrorCode error_code = ErrorCode::kPostinstallRunnerError; 12 13 if (return_code == 3) { 14 // This special return code means that we tried to update firmware, 15 // but couldn't because we booted from FW B, and we need to reboot 16 // to get back to FW A. 17 error_code = ErrorCode::kPostinstallBootedFromFirmwareB; 18 } 19 20 if (return_code == 4) { 21 // This special return code means that we tried to update firmware, 22 // but couldn't because we booted from FW B, and we need to reboot 23 // to get back to FW A. 24 error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable; 25 } 26 27 // If postinstall script for this partition is optional we can ignore the 28 // result. 29 if (install_plan_.partitions[current_partition_].postinstall_optional) { 30 LOG(INFO) << "Ignoring postinstall failure since it is optional"; 31 } else { 32 return CompletePostinstall(error_code); 33 } 34 } 35 36 accumulated_weight_ += partition_weight_[current_partition_]; 37 //将当前分区加+1 38 current_partition_++; 39 //执行报告进度 40 ReportProgress(0); 41 //继续执行下一个分区的postinstall工作 42 PerformPartitionPostinstall(); 43}
1//全部处理完成了所有分区的postinstall 2void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) { 3 // We only attempt to mark the new slot as active if all the postinstall 4 // steps succeeded. 5 // 如果所有的postinstall工作都做完了,将target分区设置为可启动 6 if (error_code == ErrorCode::kSuccess) { 7 if (install_plan_.switch_slot_on_reboot) { 8 if (!boot_control_->SetActiveBootSlot(install_plan_.target_slot)) { 9 error_code = ErrorCode::kPostinstallRunnerError; 10 } 11 } else { 12 error_code = ErrorCode::kUpdatedButNotActive; 13 } 14 } 15 16 ScopedActionCompleter completer(processor_, this); 17 completer.set_code(error_code); 18 19 if (error_code != ErrorCode::kSuccess && 20 error_code != ErrorCode::kUpdatedButNotActive) { 21 LOG(ERROR) << "Postinstall action failed."; 22 23 // Undo any changes done to trigger Powerwash. 24 if (powerwash_scheduled_) 25 hardware_->CancelPowerwash(); 26 27 return; 28 } 29 30 LOG(INFO) << "All post-install commands succeeded"; 31 //将install_plan_设置为SetOutputObject 32 if (HasOutputPipe()) { 33 SetOutputObject(install_plan_); 34 } 35}
1void PostinstallRunnerAction::SuspendAction() { 2 if (!current_command_) 3 return; 4 if (kill(current_command_, SIGSTOP) != 0) { 5 PLOG(ERROR) << "Couldn't pause child process " << current_command_; 6 } else { 7 is_current_command_suspended_ = true; 8 } 9} 10 11void PostinstallRunnerAction::ResumeAction() { 12 if (!current_command_) 13 return; 14 if (kill(current_command_, SIGCONT) != 0) { 15 PLOG(ERROR) << "Couldn't resume child process " << current_command_; 16 } else { 17 is_current_command_suspended_ = false; 18 } 19} 20 21void PostinstallRunnerAction::TerminateProcessing() { 22 if (!current_command_) 23 return; 24 // Calling KillExec() will discard the callback we registered and therefore 25 // the unretained reference to this object. 26 Subprocess::Get().KillExec(current_command_); 27 28 // If the command has been suspended, resume it after KillExec() so that the 29 // process can process the SIGTERM sent by KillExec(). 30 if (is_current_command_suspended_) { 31 ResumeAction(); 32 } 33 34 current_command_ = 0; 35 Cleanup(); 36}
PostinstallRunnerAction主要任务是对升级后的分区执行postinstall脚本,并将升级成功后的分区设置为可启动active状态
到此为止已经这基本上就是所有升级跑完的流程,我们简单的做个总结
一、InstallPlanAction
构建了一个Acion是后面三个Acion的父类,将install_plan_参数传递给DownloadAcion
二、DownloadAction(重点)
获取InstallPlanAction传递过来的install_plan_信息,构建http_fetcher_数据传输下载,使用DeltaPerformer类对象writer_的Write()将数据更新到对应分区,边数据传输,边数据写入,直到数据全部写入完成
三、FilesystemVerifierAction
挨个校验需要升级的分区的hash值,先读取当前分区的信息,定义缓冲区,计算缓冲区的hash,知道分区中所有的数据hash计算完成了,拿到整个分区的hash,与升级包中存放的升级后的分区hash值做比对,如果没问题,那就继续,如果不行,再校验基础版本的,看是基础版本的问题,还是单纯的升级失败了,
四、PostinstallRunnerAction
是对升级后的分区执行postinstall脚本,并将升级成功后的分区设置为可启动active状态
下一篇我们分析的重点是DownloadAcion的数据传输和执行写入,http_fetcher_和writer_