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.SetFileDescriptor(fd);
39 CommandFunction* cf = CommandFunctionFactory::GetInstance().GetCommandFunction(cmd.GetCommandHead());
40 if (cf == nullptr) {
41 LOG(ERROR) << "Failed to get cmd exec";
42 return false;
43 }
44 CommandResult ret = cf->Execute(cmd);
45 if (!CheckResult(ret, cmd.GetCommandLine(), cmd.GetCommandType())) {
46 return false;
47 }
48 return true;
49 }
50
JudgeBlockVerifyCmdType(Command &cmd)51 static bool JudgeBlockVerifyCmdType(Command &cmd)
52 {
53 if (cmd.GetCommandType() == CommandType::NEW ||
54 cmd.GetCommandType() == CommandType::ERASE ||
55 cmd.GetCommandType() == CommandType::FREE ||
56 cmd.GetCommandType() == CommandType::ZERO) {
57 return false;
58 }
59 return true;
60 }
61
InitCommandParser(std::vector<std::string>::const_iterator ct, std::string &retryCmd)62 std::vector<std::string>::const_iterator TransferManager::InitCommandParser(std::vector<std::string>::const_iterator ct,
63 std::string &retryCmd)
64 {
65 transferParams_->version = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
66 transferParams_->blockCount = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
67 transferParams_->maxEntries = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
68 transferParams_->maxBlocks = Utils::String2Int<size_t>(*ct++, Utils::N_DEC);
69 if (transferParams_->env != nullptr && transferParams_->env->IsRetry()) {
70 retryCmd = ReloadForRetry();
71 }
72 return ct;
73 }
74
CommandParserPreCheck(const std::vector<std::string> &context)75 bool TransferManager::CommandParserPreCheck(const std::vector<std::string> &context)
76 {
77 if (context.size() < 1) {
78 LOG(ERROR) << "too small context in transfer file";
79 return false;
80 }
81 if (transferParams_ == nullptr) {
82 LOG(ERROR) << "transferParams_ is nullptr";
83 return false;
84 }
85 return true;
86 }
87
CommandsParser(int fd, const std::vector<std::string> &context)88 bool TransferManager::CommandsParser(int fd, const std::vector<std::string> &context)
89 {
90 if (!CommandParserPreCheck(context)) {
91 return false;
92 }
93
94 std::string retryCmd = "";
95 std::vector<std::string>::const_iterator ct = context.begin();
96 ct = InitCommandParser(ct, retryCmd);
97 size_t totalSize = transferParams_->blockCount;
98 size_t initBlock = 0;
99 for (; ct != context.end(); ct++) {
100 std::unique_ptr<Command> cmd = std::make_unique<Command>(transferParams_.get());
101 if (cmd == nullptr) {
102 LOG(ERROR) << "Failed to parse command line.";
103 return false;
104 }
105 if (!cmd->Init(*ct) || transferParams_->env == nullptr) {
106 continue;
107 }
108 if (!retryCmd.empty() && transferParams_->env->IsRetry()) {
109 if (*ct == retryCmd) {
110 retryCmd.clear();
111 }
112 if (cmd->GetCommandType() != CommandType::NEW) {
113 LOG(INFO) << "Retry: Command " << *ct << " passed";
114 continue;
115 }
116 }
117 if (!transferParams_->canWrite && !JudgeBlockVerifyCmdType(*cmd)) {
118 continue;
119 }
120 if (!CommandsExecute(fd, *cmd)) {
121 LOG(ERROR) << "Running command : " << cmd->GetCommandLine() << " fail";
122 return false;
123 }
124 if (!transferParams_->canWrite) {
125 continue;
126 }
127 if (initBlock == 0) {
128 initBlock = transferParams_->written;
129 }
130 if (totalSize != 0 && (transferParams_->written - initBlock) > 0) {
131 UpdateProgress(initBlock, totalSize);
132 }
133 }
134 if (fabs(Uscript::GetScriptProportion() - 1.0f) < 1e-6) {
135 FillUpdateProgress();
136 }
137 return true;
138 }
139
UpdateProgress(size_t &initBlock, size_t totalSize)140 void TransferManager::UpdateProgress(size_t &initBlock, size_t totalSize)
141 {
142 float p = static_cast<float>(transferParams_->written - initBlock) / totalSize\
143 * Uscript::GetScriptProportion();
144 SetUpdateProgress(p);
145 initBlock = transferParams_->written;
146 }
147
RegisterForRetry(const std::string &cmd)148 bool TransferManager::RegisterForRetry(const std::string &cmd)
149 {
150 std::string path = transferParams_->retryFile;
151 int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
152 if (fd == -1) {
153 LOG(ERROR) << "Failed to create";
154 return false;
155 }
156 bool ret = Utils::WriteStringToFile(fd, cmd);
157 if (ret == false) {
158 LOG(ERROR) << "Write retry flag error";
159 }
160 fsync(fd);
161 close(fd);
162 return ret;
163 }
164
ReloadForRetry() const165 std::string TransferManager::ReloadForRetry() const
166 {
167 std::string path = transferParams_->retryFile;
168 int fd = open(path.c_str(), O_RDONLY);
169 if (fd < 0) {
170 LOG(ERROR) << "Failed to open";
171 return "";
172 }
173 (void)lseek(fd, 0, SEEK_SET);
174 std::string cmd = "";
175 if (!Utils::ReadFileToString(fd, cmd)) {
176 LOG(ERROR) << "Error to read retry flag";
177 }
178 close(fd);
179 return cmd;
180 }
181
CheckResult(const CommandResult result, const std::string &cmd, const CommandType &type)182 bool TransferManager::CheckResult(const CommandResult result, const std::string &cmd, const CommandType &type)
183 {
184 switch (result) {
185 case SUCCESS:
186 if (type != CommandType::NEW) {
187 RegisterForRetry(cmd);
188 }
189 break;
190 case NEED_RETRY:
191 LOG(INFO) << "Running command need retry!";
192 if (transferParams_->env != nullptr) {
193 transferParams_->env->PostMessage("retry_update", IO_FAILED_REBOOT);
194 }
195 return false;
196 case FAILED:
197 default:
198 LOG(ERROR) << "Running command failed";
199 return false;
200 }
201 return true;
202 }
203 } // namespace Updater
204