1 /*
2 * Copyright (c) 2021-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 #include "account_file_operator.h"
16 #include <cerrno>
17 #include <cstdio>
18 #include <fcntl.h>
19 #include <fstream>
20 #include <nlohmann/json.hpp>
21 #include <shared_mutex>
22 #include <sstream>
23 #include <string>
24 #include <sys/file.h>
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <sys/statvfs.h>
28 #include <sys/types.h>
29 #include <thread>
30 #include <unistd.h>
31 #include "account_log_wrapper.h"
32 #include "directory_ex.h"
33 #include "account_hisysevent_adapter.h"
34 namespace OHOS {
35 namespace AccountSA {
36 namespace {
37 const std::string ACCOUNT_INFO_DIGEST_FILE_PATH = "account_info_digest.json";
38 const long MAX_FILE_SIZE = 1 << 24; // 16MB
39 const unsigned long long BUFF_FILE_SIZE = 50 * 1024 * 1024; // 50MB
40 const uint32_t RETRY_TIMES = 3;
41 const uint32_t RETRY_SLEEP_MS = 5;
42 #define HMFS_MONITOR_FL 0x00000002
43 #define HMFS_IOCTL_HW_GET_FLAGS _IOR(0XF5, 70, unsigned int)
44 #define HMFS_IOCTL_HW_SET_FLAGS _IOR(0XF5, 71, unsigned int)
45 }
AccountFileOperator()46 AccountFileOperator::AccountFileOperator()
47 {}
48
~AccountFileOperator()49 AccountFileOperator::~AccountFileOperator()
50 {}
51
CreateDir(const std::string & path,mode_t mode)52 ErrCode AccountFileOperator::CreateDir(const std::string &path, mode_t mode)
53 {
54 ACCOUNT_LOGI("Start creating a directory");
55 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
56 if (!OHOS::ForceCreateDirectory(path)) {
57 ACCOUNT_LOGE("failed to create %{public}s, errno %{public}d.", path.c_str(), errno);
58 return ERR_OSACCOUNT_SERVICE_FILE_CREATE_DIR_ERROR;
59 }
60 SetDirDelFlags(path);
61 bool createFlag = OHOS::ChangeModeDirectory(path, mode);
62 if (!createFlag) {
63 ACCOUNT_LOGE("failed to change mode for %{public}s, errno %{public}d.", path.c_str(), errno);
64 return ERR_OSACCOUNT_SERVICE_FILE_CHANGE_DIR_MODE_ERROR;
65 }
66
67 return ERR_OK;
68 }
69
DeleteDirOrFile(const std::string & path)70 ErrCode AccountFileOperator::DeleteDirOrFile(const std::string &path)
71 {
72 if (IsExistDir(path)) {
73 return DeleteDir(path);
74 }
75 if (IsExistFile(path)) {
76 return DeleteFile(path);
77 }
78 ACCOUNT_LOGI("Dir or file does not exist, path %{public}s.", path.c_str());
79 return ERR_OK;
80 }
81
DeleteDir(const std::string & path)82 ErrCode AccountFileOperator::DeleteDir(const std::string &path)
83 {
84 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
85 bool delFlag = false;
86 delFlag = OHOS::ForceRemoveDirectory(path);
87 if (!delFlag) {
88 ACCOUNT_LOGE("DeleteDirOrFile failed, path %{public}s errno %{public}d.", path.c_str(), errno);
89 return ERR_OSACCOUNT_SERVICE_FILE_DELE_ERROR;
90 }
91 SetValidDeleteFileOperationFlag(path, true);
92 return ERR_OK;
93 }
94
DeleteFile(const std::string & path)95 ErrCode AccountFileOperator::DeleteFile(const std::string &path)
96 {
97 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
98 bool delFlag = false;
99 delFlag = OHOS::RemoveFile(path);
100 if (!delFlag) {
101 ACCOUNT_LOGE("DeleteDirOrFile failed, path %{public}s errno %{public}d.", path.c_str(), errno);
102 return ERR_OSACCOUNT_SERVICE_FILE_DELE_ERROR;
103 }
104 SetValidDeleteFileOperationFlag(path, true);
105 return ERR_OK;
106 }
107
SetValidModifyFileOperationFlag(const std::string & fileName,bool flag)108 void AccountFileOperator::SetValidModifyFileOperationFlag(const std::string &fileName, bool flag)
109 {
110 if (fileName.find(ACCOUNT_INFO_DIGEST_FILE_PATH) != std::string::npos) { // ignore digest file record
111 return;
112 }
113 if (!flag) {
114 validModifyFileOperationFlag_.erase(
115 std::remove(validModifyFileOperationFlag_.begin(), validModifyFileOperationFlag_.end(), fileName),
116 validModifyFileOperationFlag_.end());
117 return;
118 }
119 if (std::find(validModifyFileOperationFlag_.begin(), validModifyFileOperationFlag_.end(), fileName) ==
120 validModifyFileOperationFlag_.end()) {
121 validModifyFileOperationFlag_.emplace_back(fileName);
122 }
123 }
124
GetValidModifyFileOperationFlag(const std::string & fileName)125 bool AccountFileOperator::GetValidModifyFileOperationFlag(const std::string &fileName)
126 {
127 for (auto iter : validModifyFileOperationFlag_) {
128 if (iter == fileName) {
129 return true;
130 }
131 }
132 return false;
133 }
134
SetValidDeleteFileOperationFlag(const std::string & fileName,bool flag)135 void AccountFileOperator::SetValidDeleteFileOperationFlag(const std::string &fileName, bool flag)
136 {
137 if (!flag) {
138 validDeleteFileOperationFlag_.erase(
139 std::remove(validDeleteFileOperationFlag_.begin(), validDeleteFileOperationFlag_.end(), fileName),
140 validDeleteFileOperationFlag_.end());
141 return;
142 }
143 validDeleteFileOperationFlag_.emplace_back(fileName);
144 }
145
GetValidDeleteFileOperationFlag(const std::string & fileName)146 bool AccountFileOperator::GetValidDeleteFileOperationFlag(const std::string &fileName)
147 {
148 for (auto iter : validDeleteFileOperationFlag_) {
149 if (fileName.find(iter) != std::string::npos) {
150 return true;
151 }
152 }
153 return false;
154 }
155
IsDataStorageSufficient(const unsigned long long reqFreeBytes)156 static bool IsDataStorageSufficient(const unsigned long long reqFreeBytes)
157 {
158 struct statvfs diskInfo;
159 int ret = statvfs("/data", &diskInfo);
160 if (ret != 0) {
161 ACCOUNT_LOGE("Get disk info failed, ret=%{public}d, errno=%{public}d.", ret, errno);
162 return false;
163 }
164
165 unsigned long long freeBytes =
166 static_cast<unsigned long long>(diskInfo.f_bsize) * static_cast<unsigned long long>(diskInfo.f_bavail);
167 bool isSufficient = (freeBytes > reqFreeBytes + BUFF_FILE_SIZE);
168 if (!isSufficient) {
169 ACCOUNT_LOGE("Data storage is insufficient, freeBytes=%{public}llu, reqFreeBytes=%{public}llu.", freeBytes,
170 reqFreeBytes);
171 }
172 return isSufficient;
173 }
174
SetDirDelFlags(const std::string & dirpath)175 bool AccountFileOperator::SetDirDelFlags(const std::string &dirpath)
176 {
177 char realPath[PATH_MAX] = {0};
178 if (realpath(dirpath.c_str(), realPath) == nullptr) {
179 ACCOUNT_LOGE("Failed to get realpath");
180 return false;
181 }
182 int32_t fd = open(realPath, O_DIRECTORY);
183 if (fd < 0) {
184 ACCOUNT_LOGE("Failed to open dir, errno: %{public}d", errno);
185 return false;
186 }
187 unsigned int flags = 0;
188 int32_t ret = ioctl(fd, HMFS_IOCTL_HW_GET_FLAGS, &flags);
189 if (ret < 0) {
190 close(fd);
191 ACCOUNT_LOGE("Failed to get flags, errno: %{public}d", errno);
192 return false;
193 }
194 if (flags & HMFS_MONITOR_FL) {
195 close(fd);
196 ACCOUNT_LOGE("Delete control flag is already set");
197 return false;
198 }
199 flags |= HMFS_MONITOR_FL;
200 ret = ioctl(fd, HMFS_IOCTL_HW_SET_FLAGS, &flags);
201 if (ret < 0) {
202 close(fd);
203 ACCOUNT_LOGE("Failed to set flags, errno: %{public}d", errno);
204 return false;
205 }
206 close(fd);
207 return true;
208 }
209
InputFileByPathAndContent(const std::string & path,const std::string & content)210 ErrCode AccountFileOperator::InputFileByPathAndContent(const std::string &path, const std::string &content)
211 {
212 std::string str = path;
213 str.erase(str.rfind('/'));
214 if (!IsExistDir(str)) {
215 ErrCode errCode = CreateDir(str);
216 if (errCode != ERR_OK) {
217 ACCOUNT_LOGE("failed to create dir, str = %{public}s errCode %{public}d.", str.c_str(), errCode);
218 return errCode;
219 }
220 }
221 if (!IsDataStorageSufficient(content.length())) {
222 return ERR_ACCOUNT_COMMON_DATA_NO_SPACE;
223 }
224 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
225 SetValidModifyFileOperationFlag(path, true);
226 FILE *fp = fopen(path.c_str(), "wb");
227 if (fp == nullptr) {
228 ACCOUNT_LOGE("failed to open %{public}s, errno %{public}d.", path.c_str(), errno);
229 SetValidModifyFileOperationFlag(path, false);
230 return ERR_ACCOUNT_COMMON_FILE_OPEN_FAILED;
231 }
232 int fd = fileno(fp);
233 do {
234 flock(fd, LOCK_EX);
235 size_t num = fwrite(content.c_str(), sizeof(char), content.length(), fp);
236 if (num != content.length()) {
237 ACCOUNT_LOGE("failed to fwrite %{public}s, errno %{public}d.", path.c_str(), errno);
238 break;
239 }
240 if (fflush(fp) != 0) {
241 ACCOUNT_LOGE("failed to fflush %{public}s, errno %{public}d.", path.c_str(), errno);
242 break;
243 }
244 if (fsync(fd) != 0) {
245 ACCOUNT_LOGE("failed to fsync %{public}s, errno %{public}d.", path.c_str(), errno);
246 break;
247 }
248 flock(fd, LOCK_UN);
249 fclose(fp);
250 // change mode
251 if (!ChangeModeFile(path, S_IRUSR | S_IWUSR)) {
252 ACCOUNT_LOGW("failed to change mode for file %{public}s, errno %{public}d.", path.c_str(), errno);
253 return ERR_OHOSACCOUNT_SERVICE_FILE_CHANGE_DIR_MODE_ERROR;
254 }
255 return ERR_OK;
256 } while (0);
257 flock(fd, LOCK_UN);
258 fclose(fp);
259 SetValidModifyFileOperationFlag(path, false);
260 return ERR_ACCOUNT_COMMON_FILE_WRITE_FAILED;
261 }
262
GetFileContentByPath(const std::string & path,std::string & content)263 ErrCode AccountFileOperator::GetFileContentByPath(const std::string &path, std::string &content)
264 {
265 if (!IsExistFile(path)) {
266 ACCOUNT_LOGE("cannot find file, path = %{public}s", path.c_str());
267 return ERR_OSACCOUNT_SERVICE_FILE_FIND_FILE_ERROR;
268 }
269 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
270 FILE *fp = fopen(path.c_str(), "rb");
271 if (fp == nullptr) {
272 ACCOUNT_LOGE("cannot open file %{public}s, errno %{public}d.", path.c_str(), errno);
273 return ERR_ACCOUNT_COMMON_FILE_OPEN_FAILED;
274 }
275 int fd = fileno(fp);
276 flock(fd, LOCK_SH);
277 (void) fseek(fp, 0, SEEK_END);
278 long fileSize = ftell(fp);
279 if ((fileSize < 0) || (fileSize > MAX_FILE_SIZE)) {
280 ACCOUNT_LOGE("the file(%{public}s) size is invalid, errno %{public}d.", path.c_str(), errno);
281 flock(fd, LOCK_UN);
282 (void) fclose(fp);
283 return ERR_ACCOUNT_COMMON_FILE_READ_FAILED;
284 }
285 rewind(fp);
286 char *buffer = new (std::nothrow) char[fileSize];
287 if (buffer == nullptr) {
288 ACCOUNT_LOGE("insufficient memory");
289 flock(fd, LOCK_UN);
290 (void) fclose(fp);
291 return ERR_ACCOUNT_COMMON_INSUFFICIENT_MEMORY_ERROR;
292 }
293 size_t retSize = fread(buffer, sizeof(char), fileSize, fp);
294 if (static_cast<long>(retSize) != fileSize) {
295 ACCOUNT_LOGE("fail to read file %{public}s", path.c_str());
296 delete[] buffer;
297 flock(fd, LOCK_UN);
298 (void) fclose(fp);
299 return ERR_ACCOUNT_COMMON_FILE_READ_FAILED;
300 }
301 content = std::string(buffer, retSize);
302 delete[] buffer;
303 flock(fd, LOCK_UN);
304 (void) fclose(fp);
305 return ERR_OK;
306 }
307
IsExistFile(const std::string & path)308 bool AccountFileOperator::IsExistFile(const std::string &path)
309 {
310 if (path.empty()) {
311 return false;
312 }
313 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
314 uint32_t retryCount = 0;
315 while (retryCount < RETRY_TIMES) {
316 struct stat buf = {};
317 if (stat(path.c_str(), &buf) == 0) {
318 return S_ISREG(buf.st_mode);
319 }
320 if (errno != ENOENT) {
321 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d. Retrying...", path.c_str(), errno);
322 std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_SLEEP_MS));
323 retryCount++;
324 } else {
325 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d.", path.c_str(), errno);
326 return false;
327 }
328 }
329 return false;
330 }
331
IsJsonFormat(const std::string & path)332 bool AccountFileOperator::IsJsonFormat(const std::string &path)
333 {
334 std::string content;
335 if (GetFileContentByPath(path, content) != ERR_OK) {
336 return false;
337 }
338
339 nlohmann::json jsonData = nlohmann::json::parse(content, nullptr, false);
340 if (jsonData.is_discarded() || !jsonData.is_structured()) {
341 ACCOUNT_LOGE("File %{public}s is invalid json format, size: %{public}zu", path.c_str(), content.size());
342 return false;
343 }
344 return true;
345 }
346
IsJsonFileReady(const std::string & path)347 bool AccountFileOperator::IsJsonFileReady(const std::string &path)
348 {
349 return IsExistFile(path) && IsJsonFormat(path);
350 }
351
IsExistDir(const std::string & path)352 bool AccountFileOperator::IsExistDir(const std::string &path)
353 {
354 if (path.empty()) {
355 return false;
356 }
357 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
358 struct stat buf = {};
359 if (stat(path.c_str(), &buf) != 0) {
360 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d", path.c_str(), errno);
361 return false;
362 }
363
364 return S_ISDIR(buf.st_mode);
365 }
366 } // namespace AccountSA
367 } // namespace OHOS
368