六、A/B 升级update_engine分析-Action流程

   从上一篇我们已经知道具体的升级写入校验流程都是由InstallPlanAction,DownloadAction,FilesystemVerifierAction,PostinstallRunnerAction,这四个Action完成的,本篇主要讲解这四个的具体流程,我准备这些文件依然按照.h 和.cc进行分析

一、InstallPlanAction

二、DownloadAction

三、FilesystemVerifierAction

四、PostinstallRunnerAction

 

 

一、InstallPlanAction

 

 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<InstallPlanAction> {
 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<InstallPlanAction>::InputObjectType InputObjectType;
28  typedef ActionTraits<InstallPlanAction>::OutputObjectType OutputObjectType;
29
30 private:
31  InstallPlan install_plan_;
32
33  DISALLOW_COPY_AND_ASSIGN(InstallPlanAction);
34};

    我觉得这个installplanAcion相当于我们运了一车猪做火腿肠,生产线已经整好了,但是总的有个地方放这个生产线,所以整了个厂房放进去,切也好 剁也好,都放着里边,具体的代码也比较简单 只是把install_plan_作为SetOutputObject,也就是downloadAcion的输入,install_plan_就相当于那一车猪

 

二、DownloadAction 

我们先分析下system/update_engine/payload_consumer/download_action.h ,确认下大概的方法和定义参数

2.1 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_ptr<MultiRangeHttpFetcher> http_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<DeltaPerformer> 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

2.2 download_action.cc

2.2.1  PerformAction

 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<size_t>(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}

 

2.2.2 StartDownloading

 

 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}

 

2.2.3 ReceivedBytes

 

 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}

 

2.2.4 TransferComplete

 

 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}

 

2.2.5 TransferTerminated

 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}

 

2.2.6 SuspendAction ResumeAction TerminateProcessing

 

 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

 

三、FilesystemVerifierAction

 

3.1 filesystem_verifier_action.h

  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_ptr<HashCalculator> hasher_;
 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};

 

3.2 filesystem_verifier_action.cc

3.2.1 PerformAction

 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}

 

3.2.2 StartPartitionHashing

 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}

 

3.2.3 ScheduleRead

 1//异步读取文件系统的信息
 2void FilesystemVerifierAction::ScheduleRead() {
 3  //定义读取的字节大小取buffer_和remaining_size_的最小值
 4  //分区的大小最后的部分可能还不够buffer_的大小
 5  size_t bytes_to_read = std::min(static_cast<int64_t>(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}

 

3.2.4 OnReadDoneCallback OnReadErrorCallback

 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}

 

3.2.5 FinishPartitionHashing

 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}

 

3.2.6 Cleanup TerminateProcessing

 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值做比对,如果没问题,那就继续,如果不行,再校验基础版本的,看是基础版本的问题,还是单纯的升级失败了,

 

四、PostinstallRunnerAction

4.1 postinstall_runner_action.h

  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_progress <frac>
 48  //         <frac> should be between 0.0 and 1.0; sets the progress to the
 49  //         <frac> 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<double> 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};

 

4.2 postinstall_runner_action.cc

4.2.1 PerformAcion

 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}

4.2.2 PerformPartitionPostinstall

  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  vector<string> command = {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}

 

4.2.3 OnProgressFdReady

 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    vector<string> lines = 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}

 

4.2.4 ReportProgress

 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}

 

4.2.5 CompletePartitionPostinstall

 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}

 

4.2.6 CompletePostinstall

 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}

 

4.2.7 SuspendAction ResumeAction TerminateProcessing

 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_

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ember.js in Action is a crisp tutorial that introduces the Ember.js framework and shows you how to build production-quality web applications. You’ll begin with the basic architecture: client- and server-side MVC, integrating Ember.js with your favorite back end, handling data … and more. You’ll explore the amazing Handlebars templating engine that automatically updates your apps when the data behind them changes. Along the way, you’ll develop a complete Ember.js application and learn how to deploy, administer, and update it efficiently. Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications. About the Technology Ember.js is a JavaScript MVC framework that handles important tasks like managing code modules, maintaining state, and expediting reliable data flow. It provides the patterns, components, and scaffolding you need to build ambitious web applications. About the Book Ember.js in Action introduces the Ember.js framework and shows you how to build full-featured, desktop-quality web applications. You’ll begin with the basic architecture: client- and server-side MVC and how to integrate Ember.js with your favorite back end. Then you’ll explore the amazing Handlebars templating engine that automatically updates your apps when the data behind them changes. Along the way, you’ll develop a complete Ember.js application and learn how to deploy, administer, and update it efficiently. Readers of this book need to know JavaScript. No prior experience with Ember.js is required.
[0710/081418.767757] [INFO:main.cc(55)] A/B Update Engine starting [0710/081418.775873] [INFO:boot_control_android.cc(69)] Loaded boot control hidl hal. [0710/081418.844522] [ERROR:update_attempter_android.cc(936)] prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot) failed. [0710/081418.850761] [INFO:update_attempter_android.cc(196)] OTAResult::NOT_ATTEMPTED [0710/081418.883202] [INFO:update_attempter_android.cc(1262)] Scheduling CleanupPreviousUpdateAction. [0710/081418.895190] [INFO:action_processor.cc(51)] ActionProcessor: starting CleanupPreviousUpdateAction [0710/081418.899394] [INFO:cleanup_previous_update_action.cc(149)] Starting/resuming CleanupPreviousUpdateAction [0710/081418.902350] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 1 for WaitBootCompleted [0710/081420.904474] [INFO:cleanup_previous_update_action.cc(112)] Executing task 1 [0710/081420.906355] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 3 for WaitBootCompleted [0710/081422.908519] [INFO:cleanup_previous_update_action.cc(112)] Executing task 3 [0710/081422.910627] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 4 for WaitBootCompleted [0710/081424.915286] [INFO:cleanup_previous_update_action.cc(112)] Executing task 4 [0710/081424.919637] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 5 for WaitBootCompleted [0710/081426.923179] [INFO:cleanup_previous_update_action.cc(112)] Executing task 5 [0710/081426.927580] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 6 for WaitBootCompleted [0710/081428.930648] [INFO:cleanup_previous_update_action.cc(112)] Executing task 6 [0710/081428.936232] [INFO:cleanup_previous_update_action.cc(124)] CleanupPreviousUpdateAction scheduled task ID 7 for WaitBootCompleted [0710/081430.938563] [INFO:cleanup_previous_update_action.cc(112)] Executing task 7 [0710/081430.943791] [INFO:cleanup_previous_update_action.cc(189)] Boot completed, waiting on markBootSuccessful() [0710/081430.947213] [INFO:snapshot.cpp(3766)] EnsureMetadataMounted does nothing in Android mode. [0710/081430.950810] [INFO:snapshot_stats.cpp(37)] Read merge statistics file failed: No such file or directory [0710/081430.965136] [INFO:cleanup_previous_update_action.cc(261)] Waiting for any previous merge request to complete. This can take up to several minutes. [0710/081430.974341] [INFO:snapshot.cpp(1047)] CheckMergeState for snapshots returned: 0 [0710/081430.981751] [INFO:snapshot.cpp(1019)] ProcessUpdateState handling state: 0 [0710/081430.988898] [INFO:cleanup_previous_update_action.cc(297)] Can't find any snapshot to merge. [0710/081430.996749] [INFO:cleanup_previous_update_action.cc(130)] Stopping/suspending/completing CleanupPreviousUpdateAction [0710/081431.002327] [INFO:cleanup_previous_update_action.cc(479)] Not reporting merge stats because state is None [0710/081431.014705] [INFO:cleanup_previous_update_action.cc(130)] Stopping/suspending/completing CleanupPreviousUpdateAction [0710/081431.022443] [INFO:action_processor.cc(116)] ActionProcessor: finished last action CleanupPreviousUpdateAction with code ErrorCode::kSuccess [0710/081431.031005] [INFO:update_attempter_android.cc(570)] Processing Done. [0710/081431.038024] [INFO:update_attempter_android.cc(826)] Clearing update complete marker. [0710/081431.046821] [INFO:update_attempter_android.cc(722)] Terminating cleanup previous update. 什么意思?
07-11

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值