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
16#include "utils_directory.h"
17
18#include <cstring>
19#include <dirent.h>
20#include <sys/types.h>
21#include <system_error>
22#include <unistd.h>
23
24#include "dfs_error.h"
25#include "directory_ex.h"
26#include "hisysevent.h"
27#include "utils_log.h"
28
29namespace OHOS {
30namespace Storage {
31namespace DistributedFile {
32namespace Utils {
33using namespace std;
34
35namespace {
36    static const uint32_t STAT_MODE_DIR = 0771;
37}
38
39std::string GetAnonyString(const std::string &value)
40{
41    constexpr size_t shortIdLength = 20;
42    constexpr size_t plaintextLength = 4;
43    constexpr size_t minIdLength = 3;
44    std::string res;
45    std::string tmpStr("******");
46    size_t strLen = value.length();
47    if (strLen < minIdLength) {
48        return tmpStr;
49    }
50
51    if (strLen <= shortIdLength) {
52        res += value[0];
53        res += tmpStr;
54        res += value[strLen - 1];
55    } else {
56        res.append(value, 0, plaintextLength);
57        res += tmpStr;
58        res.append(value, strLen - plaintextLength, plaintextLength);
59    }
60    return res;
61}
62
63void SysEventWrite(string &uid)
64{
65    if (uid.empty()) {
66        LOGE("uid is empty.");
67        return;
68    }
69    int32_t ret = DEMO_SYNC_SYS_EVENT("PERMISSION_EXCEPTION",
70        HiviewDFX::HiSysEvent::EventType::SECURITY,
71        "CALLER_UID", uid,
72        "PERMISSION_NAME", "account");
73    if (ret != ERR_OK) {
74        LOGE("report PERMISSION_EXCEPTION error %{public}d", ret);
75    }
76}
77
78void SysEventFileParse(int64_t maxTime)
79{
80    int32_t ret = DEMO_SYNC_SYS_EVENT("INDEX_FILE_PARSE",
81        HiviewDFX::HiSysEvent::EventType::STATISTIC,
82        "MAX_TIME", maxTime);
83    if (ret != ERR_OK) {
84        LOGE("report INDEX_FILE_PARSE error %{public}d", ret);
85    }
86}
87
88void RadarDotsReportOpenSession(struct RadarInfo &info)
89{
90    int32_t res = ERR_OK;
91    if (info.state == StageRes::STAGE_SUCCESS) {
92        res = DEMO_SYNC_SYS_EVENT(DISTRIBUTEDFILE_CONNECT_BEHAVIOR,
93            HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
94            "ORG_PKG", ORGPKGNAME,
95            "TO_CALL_PKG", SOFTBUSNAME,
96            "FUNC", info.funcName,
97            "BIZ_SCENE", static_cast<int32_t>(BizScene::DFS_CONNECT),
98            "BIZ_STAGE", static_cast<int32_t>(BizStage::DFS_OPEN_SESSION),
99            "BIZ_STATE", static_cast<int32_t>(BizState::BIZ_STATE_START),
100            "STAGE_RES", static_cast<int32_t>(StageRes::STAGE_SUCCESS),
101            "LOCAL_SESS_NAME", info.localSessionName,
102            "PEER_SESS_NAME", info.peerSessionName);
103    } else {
104        res = DEMO_SYNC_SYS_EVENT(DISTRIBUTEDFILE_CONNECT_BEHAVIOR,
105            HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
106            "ORG_PKG", ORGPKGNAME,
107            "TO_CALL_PKG", SOFTBUSNAME,
108            "FUNC", info.funcName,
109            "BIZ_SCENE", static_cast<int32_t>(BizScene::DFS_CONNECT),
110            "BIZ_STAGE", static_cast<int32_t>(BizStage::DFS_OPEN_SESSION),
111            "BIZ_STATE", static_cast<int32_t>(BizState::BIZ_STATE_END),
112            "STAGE_RES", static_cast<int32_t>(StageRes::STAGE_FAIL),
113            "LOCAL_SESS_NAME", info.localSessionName,
114            "PEER_SESS_NAME", info.peerSessionName,
115            "ERROR_CODE", std::abs(info.errCode));
116    }
117    if (res != ERR_OK) {
118        LOGE("report RadarDotsReportOpenSession error %{public}d", res);
119    }
120}
121
122void RadarDotsOpenSession(const std::string funcName, const std::string &sessionName,
123    const std::string &peerSssionName, int32_t errCode, StageRes state)
124{
125    struct RadarInfo info = {
126        .funcName = funcName,
127        .localSessionName = sessionName,
128        .peerSessionName = peerSssionName,
129        .errCode = errCode,
130        .state = state,
131    };
132    RadarDotsReportOpenSession(info);
133}
134
135void RadarDotsReportSendFile(struct RadarInfo &info)
136{
137    int32_t res = ERR_OK;
138    if (info.state == StageRes::STAGE_SUCCESS) {
139        res = DEMO_SYNC_SYS_EVENT(DISTRIBUTEDFILE_CONNECT_BEHAVIOR,
140            HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
141            "ORG_PKG", ORGPKGNAME,
142            "TO_CALL_PKG", SOFTBUSNAME,
143            "FUNC", info.funcName,
144            "BIZ_SCENE", static_cast<int32_t>(BizScene::DFS_CONNECT),
145            "BIZ_STAGE", static_cast<int32_t>(BizStage::DFS_SENDFILE),
146            "BIZ_STATE", static_cast<int32_t>(BizState::BIZ_STATE_END),
147            "STAGE_RES", static_cast<int32_t>(StageRes::STAGE_SUCCESS),
148            "LOCAL_SESS_NAME", info.localSessionName,
149            "PEER_SESS_NAME", info.peerSessionName);
150    } else {
151        res = DEMO_SYNC_SYS_EVENT(DISTRIBUTEDFILE_CONNECT_BEHAVIOR,
152            HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
153            "ORG_PKG", ORGPKGNAME,
154            "TO_CALL_PKG", SOFTBUSNAME,
155            "FUNC", info.funcName,
156            "BIZ_SCENE", static_cast<int32_t>(BizScene::DFS_CONNECT),
157            "BIZ_STAGE", static_cast<int32_t>(BizStage::DFS_SENDFILE),
158            "BIZ_STATE", static_cast<int32_t>(BizState::BIZ_STATE_END),
159            "STAGE_RES", static_cast<int32_t>(StageRes::STAGE_FAIL),
160            "LOCAL_SESS_NAME", info.localSessionName,
161            "PEER_SESS_NAME", info.peerSessionName,
162            "ERROR_CODE", std::abs(info.errCode));
163    }
164    if (res != ERR_OK) {
165        LOGE("report RadarDotsReportSendFile error %{public}d", res);
166    }
167}
168
169void RadarDotsSendFile(const std::string funcName, const std::string &sessionName,
170    const std::string &peerSssionName, int32_t errCode, StageRes state)
171{
172    struct RadarInfo info = {
173        .funcName = funcName,
174        .localSessionName = sessionName,
175        .peerSessionName = peerSssionName,
176        .errCode = errCode,
177        .state = state,
178    };
179    RadarDotsReportSendFile(info);
180}
181
182void ForceCreateDirectory(const string &path, function<void(const string &)> onSubDirCreated)
183{
184    string::size_type index = 0;
185    do {
186        string subPath;
187        index = path.find('/', index + 1);
188        if (index == string::npos) {
189            subPath = path;
190        } else {
191            subPath = path.substr(0, index);
192        }
193
194        if (access(subPath.c_str(), F_OK) != 0) {
195            if (mkdir(subPath.c_str(), STAT_MODE_DIR) != 0) {
196                LOGE("failed to mkdir, errno:%{public}d", errno);
197                return;
198            }
199            onSubDirCreated(subPath);
200        }
201    } while (index != string::npos);
202}
203
204void ForceCreateDirectory(const string &path)
205{
206    ForceCreateDirectory(path, nullptr);
207}
208
209void ForceCreateDirectory(const string &path, mode_t mode)
210{
211    ForceCreateDirectory(path, [mode](const string &subPath) {
212        if (chmod(subPath.c_str(), mode) == -1) {
213            LOGE("failed to chmod, errno:%{public}d", errno);
214        }
215    });
216}
217
218void ForceCreateDirectory(const string &path, mode_t mode, uid_t uid, gid_t gid)
219{
220    ForceCreateDirectory(path, [mode, uid, gid](const string &subPath) {
221        if (chmod(subPath.c_str(), mode) == -1 || chown(subPath.c_str(), uid, gid) == -1) {
222            LOGE("failed to chmod or chown, errno:%{public}d", errno);
223        }
224    });
225}
226
227void ForceRemoveDirectory(const string &path)
228{
229    if (!OHOS::ForceRemoveDirectory(path)) {
230        LOGE("failed to forcibly remove directory, errno:%{public}d", errno);
231    }
232}
233
234bool ForceRemoveDirectoryDeepFirst(const string& path)
235{
236    string subPath;
237    bool ret = true;
238    DIR *dir = opendir(path.c_str());
239    if (dir == nullptr) {
240        LOGE("opendir failed, path = %{public}s, err:%{public}d", GetAnonyString(path).c_str(), errno);
241        return false;
242    }
243
244    while (true) {
245        struct dirent *ptr = readdir(dir);
246        if (ptr == nullptr) {
247            break;
248        }
249
250        // current dir OR parent dir
251        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
252            continue;
253        }
254        subPath = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
255        if (ptr->d_type == DT_DIR) {
256            if (!ForceRemoveDirectoryDeepFirst(subPath)) {
257                ret = false;
258            }
259        } else if (access(subPath.c_str(), F_OK) == 0) {
260            if (remove(subPath.c_str()) != 0) {
261                closedir(dir);
262                LOGE("remove failed, subPath = %{public}s, err:%{public}d",
263                    GetAnonyString(subPath).c_str(), errno);
264                return false;
265            }
266        }
267    }
268    closedir(dir);
269
270    string currentPath = ExcludeTrailingPathDelimiter(path);
271    if (access(currentPath.c_str(), F_OK) == 0) {
272        if (remove(currentPath.c_str()) != 0) {
273            LOGE("remove failed, currentPath = %{public}s, err:%{public}d",
274                GetAnonyString(currentPath).c_str(), errno);
275            return false;
276        }
277    }
278
279    return ret && (access(path.c_str(), F_OK) != 0);
280}
281
282bool IsFile(const std::string &path)
283{
284    if (path.empty()) {
285        return false;
286    }
287    struct stat buf = {};
288    if (stat(path.c_str(), &buf) != 0) {
289        LOGE("stat failed, errno = %{public}d", errno);
290        return false;
291    }
292    return S_ISREG(buf.st_mode);
293}
294
295bool IsFolder(const std::string &name)
296{
297    if (name.empty()) {
298        return false;
299    }
300    struct stat buf = {};
301    if (stat(name.c_str(), &buf) != 0) {
302        LOGE("stat failed, errno = %{public}d", errno);
303        return false;
304    }
305    return S_ISDIR(buf.st_mode);
306}
307
308std::vector<std::string> GetFilePath(const std::string &name)
309{
310    std::vector<std::string> path;
311    if (!IsFolder(name)) {
312        path.emplace_back(name);
313        return path;
314    }
315    auto dir = opendir(name.data());
316    struct dirent *ent = nullptr;
317    if (dir) {
318        while ((ent = readdir(dir)) != nullptr) {
319            auto tmpPath = std::string(name).append("/").append(ent->d_name);
320            if (strcmp(ent->d_name, "..") == 0 || strcmp(ent->d_name, ".") == 0) {
321                continue;
322            } else if (IsFolder(tmpPath)) {
323                auto dirPath = GetFilePath(tmpPath);
324                path.insert(path.end(), dirPath.begin(), dirPath.end());
325            } else {
326                path.emplace_back(tmpPath);
327            }
328        }
329        closedir(dir);
330    }
331    return path;
332}
333
334int32_t ChangeOwnerRecursive(const std::string &path, uid_t uid, gid_t gid)
335{
336    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), &closedir);
337    if (dir == nullptr) {
338        LOGE("Directory is null");
339        return -1;
340    }
341
342    struct dirent *entry = nullptr;
343    while ((entry = readdir(dir.get())) != nullptr) {
344        if (entry->d_type == DT_DIR) {
345            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
346                continue;
347            }
348            std::string subPath = path + "/" + entry->d_name;
349            if (chown(subPath.c_str(), uid, gid) == -1) {
350                LOGE("Change owner recursive failed");
351                return -1;
352            }
353            return ChangeOwnerRecursive(subPath, uid, gid);
354        } else {
355            std::string filePath = path + "/" + entry->d_name;
356            if (chown(filePath.c_str(), uid, gid) == -1) {
357                LOGE("Change owner recursive failed");
358                return -1;
359            }
360        }
361    }
362    return 0;
363}
364
365bool IsInt32(const nlohmann::json &jsonObj, const std::string &key)
366{
367    bool res = jsonObj.contains(key) && jsonObj[key].is_number_integer() && jsonObj[key] >= INT32_MIN &&
368        jsonObj[key] <= INT32_MAX;
369    if (!res) {
370        LOGE("the key %{public}s in jsonObj is invalid.", key.c_str());
371    }
372    return res;
373}
374} // namespace Utils
375} // namespace DistributedFile
376} // namespace Storage
377} // namespace OHOS