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