1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "applypatch/transfer_manager.h"
16 #include <fcntl.h>
17 #include <sstream>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include "applypatch/command_function.h"
21 #include "log/log.h"
22 #include "updater/updater_const.h"
23 #include "utils.h"
24 #include "applypatch/update_progress.h"
25 #include "thread_pool.h"
26
27 namespace Updater {
28 using namespace Updater::Utils;
29
TransferManager()30 TransferManager::TransferManager()
31 {
32 transferParams_ = std::make_unique<TransferParams>();
33 transferParams_->writerThreadInfo = std::make_unique<WriterThreadInfo>();
34 }
35
CommandsExecute(int fd,Command & cmd)36 bool TransferManager::CommandsExecute(int fd, Command &cmd)
37 {
38 cmd.SetSrcFileDescriptor(fd);
39 cmd.SetTargetFileDescriptor(fd);
40 CommandFunction* cf = CommandFunctionFactory::GetInstance().GetCommandFunction(cmd.GetCommandHead());
41 if (cf == nullptr) {
42 LOG(ERROR) << "Failed to get cmd exec";
43 return false;
44 }
45 CommandResult ret = cf->Execute(cmd);
46 if (!cmd.GetTransferParams()->canWrite) {
47 return ret == SUCCESS;
48 }
49 if (!CheckResult(ret, cmd.GetCommandLine(), cmd.GetCommandType())) {
50 return false;
51 }
52 return true;
53 }
54
JudgeBlockVerifyCmdType(Command & cmd)55 static bool JudgeBlockVerifyCmdType(Command &cmd)
56 {
57 if (cmd.GetCommandType() == CommandType::NEW ||
58 cmd.GetCommandType() == CommandType::ERASE ||
59 cmd.GetCommandType() == CommandType::FREE ||
60 cmd.GetCommandType() == CommandType::ZERO) {
61 return false;
62 }
63 return true;
64 }
65
InitCommandParser(std::vector<std::string>::const_iterator ct,std::string & retryCmd)66 std::vector<std::string>::const_iterator TransferManager::InitCommandParser(std::vector<std::string>::const_iterator ct,
67 std::string &retryCmd)
68 {
69 transferParams_->version = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
70 transferParams_->blockCount = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
71 transferParams_->maxEntries = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
72 transferParams_->maxBlocks = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
73 if (transferParams_->env != nullptr && transferParams_->env->IsRetry()) {
74 retryCmd = ReloadForRetry();
75 }
76 return ct;
77 }
78
CommandParserPreCheck(const std::vector<std::string> & context)79 bool TransferManager::CommandParserPreCheck(const std::vector<std::string> &context)
80 {
81 if (context.size() < 1) {
82 LOG(ERROR) << "too small context in transfer file";
83 return false;
84 }
85 if (transferParams_ == nullptr) {
86 LOG(ERROR) << "transferParams_ is nullptr";
87 return false;
88 }
89 return true;
90 }
91
CommandsParser(int fd,const std::vector<std::string> & context)92 bool TransferManager::CommandsParser(int fd, const std::vector<std::string> &context)
93 {
94 if (!CommandParserPreCheck(context)) {
95 return false;
96 }
97
98 std::string retryCmd = "";
99 std::vector<std::string>::const_iterator ct = context.begin();
100 ct = InitCommandParser(ct, retryCmd);
101 size_t totalSize = transferParams_->blockCount;
102 size_t initBlock = 0;
103 for (; ct != context.end(); ct++) {
104 std::unique_ptr<Command> cmd = std::make_unique<Command>(transferParams_.get());
105 if (cmd == nullptr) {
106 LOG(ERROR) << "Failed to parse command line.";
107 return false;
108 }
109 if (!cmd->Init(*ct) || transferParams_->env == nullptr) {
110 continue;
111 }
112 if (!retryCmd.empty() && transferParams_->env->IsRetry()) {
113 if (*ct == retryCmd) {
114 retryCmd.clear();
115 }
116 if (cmd->GetCommandType() != CommandType::NEW) {
117 LOG(INFO) << "Retry: Command " << *ct << " passed";
118 continue;
119 }
120 }
121 if (!transferParams_->canWrite && !JudgeBlockVerifyCmdType(*cmd)) {
122 continue;
123 }
124 if (!CommandsExecute(fd, *cmd)) {
125 LOG(ERROR) << "Running command : " << cmd->GetCommandLine() << " fail";
126 return false;
127 }
128 if (!transferParams_->canWrite) {
129 continue;
130 }
131 if (initBlock == 0) {
132 initBlock = transferParams_->written;
133 }
134 if (totalSize != 0 && (transferParams_->written - initBlock) > 0) {
135 UpdateProgress(initBlock, totalSize);
136 }
137 }
138 if (fabs(Uscript::GetScriptProportion() - 1.0f) < 1e-6) {
139 FillUpdateProgress();
140 }
141 return true;
142 }
143
UpdateProgress(size_t & initBlock,size_t totalSize)144 void TransferManager::UpdateProgress(size_t &initBlock, size_t totalSize)
145 {
146 float p = static_cast<float>(transferParams_->written - initBlock) / totalSize\
147 * Uscript::GetScriptProportion() * Uscript::GetTotalProportion();
148 SetUpdateProgress(p);
149 initBlock = transferParams_->written;
150 }
151
RegisterForRetry(const std::string & cmd)152 bool TransferManager::RegisterForRetry(const std::string &cmd)
153 {
154 std::string path = transferParams_->retryFile;
155 int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
156 if (fd == -1) {
157 LOG(ERROR) << "Failed to create";
158 return false;
159 }
160 bool ret = Utils::WriteStringToFile(fd, cmd);
161 if (ret == false) {
162 LOG(ERROR) << "Write retry flag error";
163 }
164 fsync(fd);
165 close(fd);
166 return ret;
167 }
168
ReloadForRetry() const169 std::string TransferManager::ReloadForRetry() const
170 {
171 std::string path = transferParams_->retryFile;
172 int fd = open(path.c_str(), O_RDONLY);
173 if (fd < 0) {
174 LOG(ERROR) << "Failed to open";
175 return "";
176 }
177 (void)lseek(fd, 0, SEEK_SET);
178 std::string cmd = "";
179 if (!Utils::ReadFileToString(fd, cmd)) {
180 LOG(ERROR) << "Error to read retry flag";
181 }
182 close(fd);
183 return cmd;
184 }
185
CheckResult(const CommandResult result,const std::string & cmd,const CommandType & type)186 bool TransferManager::CheckResult(const CommandResult result, const std::string &cmd, const CommandType &type)
187 {
188 switch (result) {
189 case SUCCESS:
190 if (type != CommandType::NEW) {
191 RegisterForRetry(cmd);
192 }
193 break;
194 case NEED_RETRY:
195 LOG(INFO) << "IO failed. Running command need retry!";
196 if (transferParams_->env != nullptr) {
197 transferParams_->env->PostMessage("retry_update", IO_FAILED_REBOOT);
198 }
199 return false;
200 case FAILED:
201 LOG(INFO) << "Block update failed. Running command need retry!";
202 if (transferParams_->env != nullptr) {
203 transferParams_->env->PostMessage("retry_update", BLOCK_UPDATE_FAILED_REBOOT);
204 }
205 return false;
206 default:
207 LOG(ERROR) << "Running command failed";
208 return false;
209 }
210 return true;
211 }
212 } // namespace Updater
213