1/*
2 * Copyright (c) 2022 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 "file_util.h"
16
17#include <dirent.h>
18#include <fstream>
19#include <iostream>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <sys/xattr.h>
23#include <unistd.h>
24
25namespace OHOS {
26namespace HiviewDFX {
27namespace FileUtil {
28namespace {
29const char PATH_DELIMITER = '/';
30constexpr mode_t FILE_PERM_600 = S_IRUSR | S_IWUSR;
31}
32bool IsFileExists(const std::string& file)
33{
34    return access(file.c_str(), F_OK) == 0;
35}
36
37bool IsFile(const std::string& file)
38{
39    struct stat statBuf {};
40    return lstat(file.c_str(), &statBuf) == 0 ? S_ISREG(statBuf.st_mode) : false;
41}
42
43bool IsDirectory(const std::string& dir)
44{
45    struct stat statBuf {};
46    return lstat(dir.c_str(), &statBuf) == 0 ? S_ISDIR(statBuf.st_mode) : false;
47}
48
49bool RemoveFile(const std::string& file)
50{
51    return !IsFileExists(file) || (remove(file.c_str()) == 0);
52}
53
54bool RemoveDirectory(const std::string& dir)
55{
56    return !IsFileExists(dir) || (rmdir(dir.c_str()) == 0);
57}
58
59bool ForceCreateDirectory(const std::string& dir)
60{
61    std::string::size_type index = 0;
62    do {
63        std::string subPath;
64        index = dir.find('/', index + 1); // (index + 1) means the next char traversed
65        if (index == std::string::npos) {
66            subPath = dir;
67        } else {
68            subPath = dir.substr(0, index);
69        }
70
71        if (!IsFileExists(subPath) && mkdir(subPath.c_str(), S_IRWXU) != 0) {
72            return false;
73        }
74    } while (index != std::string::npos);
75    return IsFileExists(dir);
76}
77
78bool ForceRemoveDirectory(const std::string& dir, bool isDeleteSelf)
79{
80    if (IsFile(dir)) {
81        return RemoveFile(dir);
82    } else if (IsDirectory(dir)) {
83        DIR* dirPtr = opendir(dir.c_str());
84        if (dirPtr == nullptr) {
85            return false;
86        }
87        struct dirent* dirInfo = nullptr;
88        while ((dirInfo = readdir(dirPtr)) != nullptr) {
89            // do not process the special dir
90            if (strcmp(dirInfo->d_name, ".") == 0 || strcmp(dirInfo->d_name, "..") == 0) {
91                continue;
92            }
93            std::string filePath = GetFilePathByDir(dir, dirInfo->d_name);
94            if (!ForceRemoveDirectory(filePath)) {
95                closedir(dirPtr);
96                return false;
97            }
98        }
99        closedir(dirPtr);
100        if (isDeleteSelf && !RemoveDirectory(dir)) {
101            return false;
102        }
103    } else {
104        return false;
105    }
106    return true;
107}
108
109void GetDirFiles(const std::string& dir, std::vector<std::string>& files)
110{
111    DIR* dirPtr = opendir(dir.c_str());
112    if (dirPtr == nullptr) {
113        return;
114    }
115
116    struct dirent* ent = nullptr;
117    while ((ent = readdir(dirPtr)) != nullptr) {
118        if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
119            continue;
120        }
121        std::string filePath = GetFilePathByDir(dir, ent->d_name);
122        if (IsFile(filePath)) { // do not process subdirectory
123            files.push_back(filePath);
124        }
125    }
126    closedir(dirPtr);
127}
128
129uint64_t GetDirSize(const std::string& dir)
130{
131    std::vector<std::string> files;
132    GetDirFiles(dir, files);
133    uint64_t totalSize = 0;
134    struct stat statBuf {};
135    for (auto& file : files) {
136        if (stat(file.c_str(), &statBuf) == 0) {
137            totalSize += static_cast<uint64_t>(statBuf.st_size);
138        }
139    }
140    return totalSize;
141}
142
143uint64_t GetFileSize(const std::string& file)
144{
145    struct stat statBuf {};
146    return stat(file.c_str(), &statBuf) == 0 ? static_cast<uint64_t>(statBuf.st_size) : 0;
147}
148
149bool ChangeMode(const std::string& file, const mode_t& mode)
150{
151    return (chmod(file.c_str(), mode) == 0);
152}
153
154bool CreateFile(const std::string& file, const mode_t& mode)
155{
156    if (IsFileExists(file)) {
157        return true;
158    }
159    std::ofstream fout(file);
160    if (!fout.is_open()) {
161        return false;
162    }
163    fout.flush();
164    fout.close();
165    return ChangeMode(file, mode);
166}
167
168bool SaveStringToFile(const std::string& file, const std::string& content, bool isTrunc)
169{
170    if (content.empty()) {
171        return true;
172    }
173
174    if (!CreateFile(file, FILE_PERM_600)) {
175        return false;
176    }
177    std::ofstream os;
178    if (isTrunc) {
179        os.open(file.c_str(), std::ios::out | std::ios::trunc);
180    } else {
181        os.open(file.c_str(), std::ios::out | std::ios::app);
182    }
183    if (!os.is_open()) {
184        return false;
185    }
186
187    os.write(content.c_str(), content.length());
188    if (os.fail()) {
189        os.close();
190        return false;
191    }
192    os.close();
193    return true;
194}
195
196std::string GetFilePathByDir(const std::string& dir, const std::string& fileName)
197{
198    if (dir.empty()) {
199        return fileName;
200    }
201    std::string filePath = dir;
202    if (filePath.back() != '/') {
203        filePath.push_back(PATH_DELIMITER);
204    }
205    filePath.append(fileName);
206    return filePath;
207}
208
209bool LoadLinesFromFile(const std::string& filePath, std::vector<std::string>& lines)
210{
211    std::ifstream file(filePath);
212    if (file.is_open()) {
213        std::string line;
214        while (std::getline(file, line)) {
215            lines.emplace_back(line);
216        }
217        file.close();
218        return true;
219    }
220    return false;
221}
222
223bool SetDirXattr(const std::string& dir, const std::string& name, const std::string& value)
224{
225    return setxattr(dir.c_str(), name.c_str(), value.c_str(), strlen(value.c_str()), 0) == 0;
226}
227} // namespace FileUtil
228} // namespace HiviewDFX
229} // namespace OHOS
230