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 "host_app.h"
16 #include "compress.h"
17 
18 namespace Hdc {
HdcHostApp(HTaskInfo hTaskInfo)19 HdcHostApp::HdcHostApp(HTaskInfo hTaskInfo)
20     : HdcTransferBase(hTaskInfo)
21 {
22     commandBegin = CMD_APP_BEGIN;
23     commandData = CMD_APP_DATA;
24     originLocalDir = "";
25     isStableBuf = hTaskInfo->isStableBuf;
26 }
27 
~HdcHostApp()28 HdcHostApp::~HdcHostApp()
29 {
30 }
31 
Dir2Tar(const char *dir)32 string HdcHostApp::Dir2Tar(const char *dir)
33 {
34     WRITE_LOG(LOG_DEBUG, "dir:%s", dir);
35     string tarpath;
36     uv_fs_t req;
37     int r = uv_fs_lstat(nullptr, &req, dir, nullptr);
38     uv_fs_req_cleanup(&req);
39     if (r == 0 && (req.statbuf.st_mode & S_IFDIR)) {  // is dir
40         string sdir = dir;
41         string tarname = Base::GetRandomString(EXPECTED_LEN) + ".tar";
42         tarpath = Base::GetTmpDir() + tarname;
43         WRITE_LOG(LOG_DEBUG, "tarpath:%s", tarpath.c_str());
44         Compress c;
45         c.UpdataPrefix(sdir);
46         c.AddPath(sdir);
47         c.SaveToFile(tarpath);
48     }
49     return tarpath;
50 }
51 
BeginInstall(CtxFile *context, const char *command)52 bool HdcHostApp::BeginInstall(CtxFile *context, const char *command)
53 {
54     int argc = 0;
55     bool ret = false;
56     string options;
57     char **argv = Base::SplitCommandToArgs(command, &argc);
58     if (argc < 1) {
59         goto Finish;
60     }
61 
62     for (int i = 0; i < argc; ++i) {
63         if (!strcmp(argv[i], CMD_OPTION_CLIENTCWD.c_str())) {
64             if (i + 1 < argc) {
65                 context->transferConfig.clientCwd = argv[i + 1];
66                 i += 1;  // add content index
67             }
68         } else if (!strncmp(argv[i], "-", 1)) {
69             if (options.size()) {
70                 options += " ";
71             }
72             options += argv[i];
73         } else {
74             string path = argv[i];
75             ExtractRelativePath(context->transferConfig.clientCwd, path);
76             if (MatchPackageExtendName(path, ".hap") || MatchPackageExtendName(path, ".hsp")) {
77                 context->taskQueue.push_back(path);
78             } else {
79                 string tarpath = Dir2Tar(path.c_str());
80                 if (!tarpath.empty()) {
81                     context->taskQueue.push_back(tarpath);
82                     originLocalDir = path;
83                 }
84             }
85         }
86     }
87     if (!context->taskQueue.size()) {
88         LogMsg(MSG_FAIL, "Not any installation package was found");
89         goto Finish;
90     }
91     // remove repeate
92     sort(context->taskQueue.begin(), context->taskQueue.end());
93     context->taskQueue.erase(unique(context->taskQueue.begin(), context->taskQueue.end()), context->taskQueue.end());
94 
95     context->transferConfig.options = options;
96     context->transferConfig.functionName = CMDSTR_APP_INSTALL;
97     ret = RunQueue(context);
98 Finish:
99     if (argv) {
100         delete[](reinterpret_cast<char *>(argv));
101     }
102     return ret;
103 }
104 
BeginSideload(CtxFile *context, const char *localPath)105 bool HdcHostApp::BeginSideload(CtxFile *context, const char *localPath)
106 {
107     bool ret = false;
108     context->transferConfig.functionName = CMDSTR_APP_SIDELOAD;
109     context->taskQueue.push_back(localPath);
110     ret = RunQueue(context);
111     return ret;
112 }
113 
RunQueue(CtxFile *context)114 bool HdcHostApp::RunQueue(CtxFile *context)
115 {
116     context->localPath = context->taskQueue.back();
117     uv_fs_t *openReq = new uv_fs_t;
118     if (openReq == nullptr) {
119         LogMsg(MSG_FAIL, "HdcHostApp::RunQueue new uv_fs_t failed");
120         OnFileOpenFailed(context);
121         return false;
122     }
123     memset_s(openReq, sizeof(uv_fs_t), 0, sizeof(uv_fs_t));
124     openReq->data = context;
125     ++refCount;
126     uv_fs_open(loopTask, openReq, context->localPath.c_str(), O_RDONLY, 0, OnFileOpen);
127     context->master = true;
128     return true;
129 }
130 
CheckMaster(CtxFile *context)131 void HdcHostApp::CheckMaster(CtxFile *context)
132 {
133     uv_fs_t fs = {};
134     uv_fs_fstat(nullptr, &fs, context->openFd, nullptr);
135     context->transferConfig.fileSize = fs.statbuf.st_size;
136     uv_fs_req_cleanup(&fs);
137 
138     context->transferConfig.optionalName
139         = Base::GetRandomString(EXPECTED_LEN);  // Prevent the name of illegal APP leads to pm unable to install
140     if (context->localPath.find(".hap") != static_cast<size_t>(-1)) {
141         context->transferConfig.optionalName += ".hap";
142     } else if (context->localPath.find(".hsp") != static_cast<size_t>(-1)) {
143         context->transferConfig.optionalName += ".hsp";
144     } else if (context->localPath.find(".tar") != static_cast<size_t>(-1)) {
145         context->transferConfig.optionalName += ".tar";
146     } else {
147         context->transferConfig.optionalName += ".bundle";
148     }
149     string bufString = SerialStruct::SerializeToString(context->transferConfig);
150     SendToAnother(CMD_APP_CHECK, const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(bufString.c_str())),
151                   bufString.size());
152 }
153 
CheckInstallContinue(AppModType mode, bool lastResult, const char *msg)154 bool HdcHostApp::CheckInstallContinue(AppModType mode, bool lastResult, const char *msg)
155 {
156     string modeDesc;
157     switch (mode) {
158         case APPMOD_INSTALL:
159             modeDesc = "App install";
160             break;
161         case APPMOD_UNINSTALL:
162             modeDesc = "App uninstall";
163             break;
164         case APPMOD_SIDELOAD:
165             modeDesc = "Side load";
166             break;
167         default:
168             modeDesc = "Unknown";
169             break;
170     }
171     if (ctxNow.taskQueue.size() > 0) {
172         string path = ctxNow.taskQueue.back();
173         ctxNow.taskQueue.pop_back();
174         string::size_type pos = path.rfind(".tar");
175         if (mode == APPMOD_INSTALL && pos != string::npos) {
176             unlink(path.c_str());
177             WRITE_LOG(LOG_DEBUG, "unlink path:%s", path.c_str());
178         }
179     }
180     string path = ctxNow.localPath;
181     if (!originLocalDir.empty()) {
182         path = originLocalDir;
183     }
184     LogMsg(MSG_INFO, "%s path:%s msg:%s", modeDesc.c_str(), path.c_str(), msg + printedMsgLen);
185     printedMsgLen = strlen(msg);
186     if (singalStop || !ctxNow.taskQueue.size()) {
187         LogMsg(MSG_OK, "AppMod finish");
188         return false;
189     }
190     return RunQueue(&ctxNow);
191 }
192 
CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)193 bool HdcHostApp::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
194 {
195     if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
196         return false;
197     }
198     bool ret = true;
199     constexpr int cmdOffset = 2;
200     switch (command) {
201         case CMD_APP_INIT: {
202             ret = BeginInstall(&ctxNow, (const char *)payload);
203             break;
204         }
205         case CMD_APP_FINISH: {
206             AppModType mode = static_cast<AppModType>(payload[0]);
207             bool result = static_cast<bool>(payload[1]);
208             string s(reinterpret_cast<char *>(payload + cmdOffset), payloadSize - cmdOffset);
209             ret = CheckInstallContinue(mode, result, s.c_str());
210             break;
211         }
212         case CMD_APP_UNINSTALL: {
213             SendToAnother(CMD_APP_UNINSTALL, payload, payloadSize);
214             ctxNow.taskQueue.push_back(reinterpret_cast<char *>(payload));  // just compatible
215             break;
216         }
217         case CMD_APP_SIDELOAD: {
218             BeginSideload(&ctxNow, (const char *)payload);
219             break;
220         }
221         default:
222             break;
223     }
224     return ret;
225 };
226 }
227