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