1 /*
2 * Copyright (c) 2024 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
16 #include "cj_request_task.h"
17 #include <cstring>
18 #include <fcntl.h>
19 #include <filesystem>
20 #include <fstream>
21 #include <regex>
22 #include <string>
23 #include <sys/stat.h>
24 #include "application_context.h"
25 #include "cj_app_state_callback.h"
26 #include "cj_initialize.h"
27 #include "cj_lambda.h"
28 #include "cj_request_common.h"
29 #include "cj_request_event.h"
30 #include "cj_response_listener.h"
31 #include "constant.h"
32 #include "js_common.h"
33 #include "log.h"
34 #include "request_manager.h"
35 #include "securec.h"
36 #include "storage_acl.h"
37
38 namespace OHOS::CJSystemapi::Request {
39 namespace fs = std::filesystem;
40 using OHOS::AbilityRuntime::Context;
41 using OHOS::Request::Action;
42 using OHOS::Request::ExceptionErrorCode;
43 using OHOS::Request::RequestManager;
44 using OHOS::Request::TaskInfo;
45 using OHOS::Request::Version;
46 using OHOS::StorageDaemon::AclSetAccess;
47
48 std::mutex CJRequestTask::taskMutex_;
49 std::map<std::string, CJRequestTask *> CJRequestTask::taskMap_;
50
51 std::mutex CJRequestTask::pathMutex_;
52 std::map<std::string, int32_t> CJRequestTask::pathMap_;
53
54 bool CJRequestTask::register_ = false;
55
56 static constexpr int ACL_SUCC = 0;
57 static const std::string SA_PERMISSION_RWX = "g:3815:rwx";
58 static const std::string SA_PERMISSION_X = "g:3815:x";
59 static const std::string SA_PERMISSION_CLEAN = "g:3815:---";
60
CJRequestTask()61 CJRequestTask::CJRequestTask()
62 {
63 config_.version = Version::API10;
64 config_.action = Action::ANY;
65 REQUEST_HILOGD("construct CJRequestTask()");
66 }
67
~CJRequestTask()68 CJRequestTask::~CJRequestTask()
69 {
70 REQUEST_HILOGD("~CJRequestTask()");
71 RequestManager::GetInstance()->RemoveAllListeners(GetTidStr());
72 }
73
GetTidStr() const74 std::string CJRequestTask::GetTidStr() const
75 {
76 return tid_;
77 }
78
SetTid()79 void CJRequestTask::SetTid()
80 {
81 tid_ = taskId_;
82 }
83
AddTaskMap(const std::string & key,CJRequestTask * task)84 void CJRequestTask::AddTaskMap(const std::string &key, CJRequestTask *task)
85 {
86 std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
87 CJRequestTask::taskMap_[key] = task;
88 }
89
FindTaskById(std::string & taskId)90 CJRequestTask *CJRequestTask::FindTaskById(std::string &taskId)
91 {
92 CJRequestTask *task = nullptr;
93 {
94 std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
95 auto item = CJRequestTask::taskMap_.find(taskId);
96 if (item == CJRequestTask::taskMap_.end()) {
97 return nullptr;
98 }
99 task = item->second;
100 }
101 return task;
102 }
103
ClearTaskMap(const std::string & key)104 CJRequestTask *CJRequestTask::ClearTaskMap(const std::string &key)
105 {
106 std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
107 auto it = taskMap_.find(key);
108 if (it == taskMap_.end()) {
109 return nullptr;
110 }
111 taskMap_.erase(it);
112 return it->second;
113 }
114
SetPathPermission(const std::string & filepath)115 bool CJRequestTask::SetPathPermission(const std::string &filepath)
116 {
117 std::string baseDir;
118 if (!CJInitialize::GetBaseDir(baseDir) || filepath.find(baseDir) == std::string::npos) {
119 REQUEST_HILOGE("File dir not found.");
120 return false;
121 }
122
123 AddPathMap(filepath, baseDir);
124 {
125 std::lock_guard<std::mutex> lockGuard(pathMutex_);
126 for (auto it : pathMap_) {
127 if (it.second <= 0) {
128 continue;
129 }
130 if (AclSetAccess(it.first, SA_PERMISSION_X) != ACL_SUCC) {
131 REQUEST_HILOGE("AclSetAccess Parent Dir Failed.");
132 }
133 }
134 }
135
136 if (AclSetAccess(filepath, SA_PERMISSION_RWX) != ACL_SUCC) {
137 REQUEST_HILOGE("AclSetAccess Child Dir Failed.");
138 return false;
139 }
140 return true;
141 }
142
SetDirsPermission(std::vector<std::string> & dirs)143 bool CJRequestTask::SetDirsPermission(std::vector<std::string> &dirs)
144 {
145 if (dirs.empty()) {
146 return true;
147 }
148 std::string newPath = "/data/storage/el2/base/.ohos/.request/.certs";
149 std::vector<std::string> dirElems;
150 CJInitialize::StringSplit(newPath, '/', dirElems);
151 if (!CJInitialize::CreateDirs(dirElems)) {
152 REQUEST_HILOGE("CreateDirs Err: %{public}s", newPath.c_str());
153 return false;
154 }
155
156 for (const auto &folderPath : dirs) {
157 fs::path folder = folderPath;
158 if (!(fs::exists(folder) && fs::is_directory(folder))) {
159 return false;
160 }
161 for (const auto &entry : fs::directory_iterator(folder)) {
162 fs::path path = entry.path();
163 std::string existfilePath = folder.string() + "/" + path.filename().string();
164 std::string newfilePath = newPath + "/" + path.filename().string();
165 if (!fs::exists(newfilePath)) {
166 fs::copy(existfilePath, newfilePath);
167 }
168 if (chmod(newfilePath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
169 REQUEST_HILOGD("File add OTH access Failed.");
170 }
171 REQUEST_HILOGD("current filePath is %{public}s", newfilePath.c_str());
172 if (!CJRequestTask::SetPathPermission(newfilePath)) {
173 REQUEST_HILOGE("Set path permission fail.");
174 return false;
175 }
176 }
177 }
178 if (!dirs.empty()) {
179 dirs.clear();
180 dirs.push_back(newPath);
181 }
182
183 return true;
184 }
185
AddPathMap(const std::string & filepath,const std::string & baseDir)186 void CJRequestTask::AddPathMap(const std::string &filepath, const std::string &baseDir)
187 {
188 std::string childDir(filepath);
189 std::string parentDir;
190 while (childDir.length() > baseDir.length()) {
191 parentDir = childDir.substr(0, childDir.rfind("/"));
192 std::lock_guard<std::mutex> lockGuard(CJRequestTask::pathMutex_);
193 auto it = pathMap_.find(parentDir);
194 if (it == pathMap_.end()) {
195 pathMap_[parentDir] = 1;
196 } else {
197 pathMap_[parentDir] += 1;
198 }
199 childDir = parentDir;
200 }
201 }
202
ResetDirAccess(const std::string & filepath)203 void CJRequestTask::ResetDirAccess(const std::string &filepath)
204 {
205 int ret = AclSetAccess(filepath, SA_PERMISSION_CLEAN);
206 if (ret != ACL_SUCC) {
207 REQUEST_HILOGE("AclSetAccess Reset Dir Failed: %{public}s", filepath.c_str());
208 }
209 }
210
RemovePathMap(const std::string & filepath)211 void CJRequestTask::RemovePathMap(const std::string &filepath)
212 {
213 std::string baseDir;
214 if (!CJInitialize::GetBaseDir(baseDir) || filepath.find(baseDir) == std::string::npos) {
215 REQUEST_HILOGE("File dir not found.");
216 return;
217 }
218
219 if (chmod(filepath.c_str(), S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
220 REQUEST_HILOGE("File remove WOTH access Failed.");
221 }
222
223 std::string childDir(filepath);
224 std::string parentDir;
225 while (childDir.length() > baseDir.length()) {
226 parentDir = childDir.substr(0, childDir.rfind("/"));
227 std::lock_guard<std::mutex> lockGuard(CJRequestTask::pathMutex_);
228 auto it = pathMap_.find(parentDir);
229 if (it != pathMap_.end()) {
230 if (pathMap_[parentDir] <= 1) {
231 pathMap_.erase(parentDir);
232 ResetDirAccess(parentDir);
233 } else {
234 pathMap_[parentDir] -= 1;
235 }
236 }
237 childDir = parentDir;
238 }
239 }
240
RemoveDirsPermission(const std::vector<std::string> & dirs)241 void CJRequestTask::RemoveDirsPermission(const std::vector<std::string> &dirs)
242 {
243 for (const auto &folderPath : dirs) {
244 fs::path folder = folderPath;
245 for (const auto &entry : fs::directory_iterator(folder)) {
246 fs::path path = entry.path();
247 std::string filePath = folder.string() + "/" + path.filename().string();
248 RemovePathMap(filePath);
249 }
250 }
251 }
252
RegisterForegroundResume()253 void CJRequestTask::RegisterForegroundResume()
254 {
255 if (register_) {
256 return;
257 }
258 register_ = true;
259 auto context = AbilityRuntime::ApplicationContext::GetInstance();
260 if (context == nullptr) {
261 REQUEST_HILOGE("Get ApplicationContext failed");
262 return;
263 }
264 context->RegisterAbilityLifecycleCallback(std::make_shared<CJAppStateCallback>());
265 REQUEST_HILOGD("Register foreground resume callback success");
266 }
267
Create(Context * context,Config & config)268 ExceptionError CJRequestTask::Create(Context *context, Config &config)
269 {
270 int32_t seq = RequestManager::GetInstance()->GetNextSeq();
271 REQUEST_HILOGI("Begin task create, seq: %{public}d", seq);
272 config_ = config;
273
274 ExceptionError err;
275 RequestManager::GetInstance()->RestoreListener(CJRequestTask::ReloadListener);
276
277 if (config.mode == Mode::FOREGROUND) {
278 RegisterForegroundResume();
279 }
280
281 int32_t ret = RequestManager::GetInstance()->Create(config_, seq, taskId_);
282 if (ret != ExceptionErrorCode::E_OK) {
283 REQUEST_HILOGE("Create task failed, in");
284 err.code = static_cast<ExceptionErrorCode>(ret);
285 return err;
286 }
287
288 SetTid();
289 listenerMutex_.lock();
290 notifyDataListenerMap_[SubscribeType::REMOVE] =
291 std::make_shared<CJNotifyDataListener>(GetTidStr(), SubscribeType::REMOVE);
292 listenerMutex_.unlock();
293 RequestManager::GetInstance()->AddListener(GetTidStr(), SubscribeType::REMOVE,
294 notifyDataListenerMap_[SubscribeType::REMOVE]);
295
296 AddTaskMap(GetTidStr(), this);
297
298 return err;
299 }
300
GetTask(OHOS::AbilityRuntime::Context * context,std::string & taskId,std::string & token,Config & config)301 ExceptionError CJRequestTask::GetTask(OHOS::AbilityRuntime::Context *context, std::string &taskId, std::string &token,
302 Config &config)
303 {
304 ExceptionError err;
305 int32_t seq = RequestManager::GetInstance()->GetNextSeq();
306 REQUEST_HILOGI("Begin get task, seq: %{public}d", seq);
307
308 CJRequestTask *task = CJRequestTask::FindTaskById(taskId);
309 if (task != nullptr) {
310 if (task->config_.token != token) {
311 return ConvertError(ExceptionErrorCode::E_TASK_NOT_FOUND);
312 }
313 config = task->config_;
314 return err;
315 }
316
317 int32_t result = RequestManager::GetInstance()->GetTask(taskId, token, config);
318 if (result != ExceptionErrorCode::E_OK) {
319 return ConvertError(result);
320 }
321 return err;
322 }
323
Remove(const std::string & tid)324 ExceptionError CJRequestTask::Remove(const std::string &tid)
325 {
326 int32_t result = RequestManager::GetInstance()->Remove(tid, Version::API10);
327 if (result != ExceptionErrorCode::E_OK) {
328 return ConvertError(result);
329 }
330
331 return ExceptionError();
332 }
333
Touch(const std::string & tid,TaskInfo & task,const std::string & token)334 ExceptionError CJRequestTask::Touch(const std::string &tid, TaskInfo &task, const std::string &token)
335 {
336 ExceptionError err;
337 int32_t result = RequestManager::GetInstance()->Touch(tid, token, task);
338 if (result != ExceptionErrorCode::E_OK) {
339 return ConvertError(result);
340 }
341 return err;
342 }
343
Search(const Filter & filter,std::vector<std::string> & tids)344 ExceptionError CJRequestTask::Search(const Filter &filter, std::vector<std::string> &tids)
345 {
346 ExceptionError err;
347 int32_t result = RequestManager::GetInstance()->Search(filter, tids);
348 if (result != ExceptionErrorCode::E_OK) {
349 return ConvertError(result);
350 }
351
352 return err;
353 }
354
ReloadListener()355 void CJRequestTask::ReloadListener()
356 {
357 REQUEST_HILOGD("ReloadListener in");
358 std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
359 RequestManager::GetInstance()->ReopenChannel();
360 for (const auto &it : taskMap_) {
361 RequestManager::GetInstance()->Subscribe(it.first);
362 }
363 }
364
On(std::string type,std::string & taskId,void * callback)365 ExceptionError CJRequestTask::On(std::string type, std::string &taskId, void *callback)
366 {
367 int32_t seq = RequestManager::GetInstance()->GetNextSeq();
368 REQUEST_HILOGI("Begin task on, seq: %{public}d", seq);
369
370 ExceptionError err;
371 SubscribeType subscribeType = CJRequestEvent::StringToSubscribeType(type);
372 if (subscribeType == SubscribeType::BUTT) {
373 err.code = ExceptionErrorCode::E_PARAMETER_CHECK;
374 err.errInfo = "First parameter error";
375 return err;
376 }
377
378 if (subscribeType == SubscribeType::RESPONSE) {
379 listenerMutex_.lock();
380 if (responseListener_ == nullptr) {
381 responseListener_ = std::make_shared<CJResponseListener>(GetTidStr());
382 }
383 listenerMutex_.unlock();
384 responseListener_->AddListener(CJLambda::Create((void (*)(CResponse progress))callback), callback);
385 } else {
386 listenerMutex_.lock();
387 auto listener = notifyDataListenerMap_.find(subscribeType);
388 if (listener == notifyDataListenerMap_.end()) {
389 notifyDataListenerMap_[subscribeType] = std::make_shared<CJNotifyDataListener>(GetTidStr(), subscribeType);
390 }
391 listenerMutex_.unlock();
392 notifyDataListenerMap_[subscribeType]->AddListener(CJLambda::Create((void (*)(CProgress progress))callback),
393 (CFunc)callback);
394 }
395
396 REQUEST_HILOGI("End task on event %{public}s successfully, seq: %{public}d, tid: %{public}s", type.c_str(), seq,
397 GetTidStr().c_str());
398
399 return err;
400 }
401
Off(std::string event,CFunc callback)402 ExceptionError CJRequestTask::Off(std::string event, CFunc callback)
403 {
404 int32_t seq = RequestManager::GetInstance()->GetNextSeq();
405 REQUEST_HILOGI("Begin task off, seq: %{public}d", seq);
406
407 ExceptionError err;
408 SubscribeType subscribeType = CJRequestEvent::StringToSubscribeType(event);
409 if (subscribeType == SubscribeType::BUTT) {
410 err.code = ExceptionErrorCode::E_PARAMETER_CHECK;
411 err.errInfo = "First parameter error";
412 return err;
413 }
414
415 listenerMutex_.lock();
416 if (subscribeType == SubscribeType::RESPONSE) {
417 if (responseListener_ == nullptr) {
418 responseListener_ = std::make_shared<CJResponseListener>(GetTidStr());
419 }
420 } else {
421 auto listener = notifyDataListenerMap_.find(subscribeType);
422 if (listener == notifyDataListenerMap_.end()) {
423 notifyDataListenerMap_[subscribeType] = std::make_shared<CJNotifyDataListener>(GetTidStr(), subscribeType);
424 }
425 }
426 listenerMutex_.unlock();
427 notifyDataListenerMap_[subscribeType]->RemoveListener((CFunc)callback);
428
429 return err;
430 }
431
ClearTaskTemp(const std::string & tid,bool isRmFiles,bool isRmAcls,bool isRmCertsAcls)432 void CJRequestTask::ClearTaskTemp(const std::string &tid, bool isRmFiles, bool isRmAcls, bool isRmCertsAcls)
433 {
434 std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
435 auto item = CJRequestTask::taskMap_.find(tid);
436 if (item == CJRequestTask::taskMap_.end()) {
437 REQUEST_HILOGD("Clear task tmp files, not find task");
438 return;
439 }
440 auto task = item->second;
441 if (isRmFiles) {
442 auto bodyFileNames = task->config_.bodyFileNames;
443 for (auto &filePath : bodyFileNames) {
444 RemovePathMap(filePath);
445 RemoveFile(filePath);
446 }
447 }
448 if (isRmAcls) {
449 // Reset Acl permission
450 for (auto &file : task->config_.files) {
451 RemovePathMap(file.uri);
452 }
453 }
454 if (isRmCertsAcls) {
455 RemoveDirsPermission(task->config_.certsPath);
456 }
457 }
458
459 } // namespace OHOS::CJSystemapi::Request