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 #ifdef ENABLE_FILE_WATCHER
38 constexpr char ACCOUNT_INFO_DIGEST_FILE_PATH[] = "account_info_digest.json";
39 #endif // ENABLE_FILE_WATCHER
40 const long MAX_FILE_SIZE = 16 * 1024 * 1024; // 16MB
41 const unsigned long long BUFF_FILE_SIZE = 50 * 1024 * 1024; // 50MB
42 const uint32_t RETRY_TIMES = 3;
43 const uint32_t RETRY_SLEEP_MS = 5;
44 #define HMFS_MONITOR_FL 0x00000002
45 #define HMFS_IOCTL_HW_GET_FLAGS _IOR(0XF5, 70, unsigned int)
46 #define HMFS_IOCTL_HW_SET_FLAGS _IOR(0XF5, 71, unsigned int)
47 }
AccountFileOperator()48 AccountFileOperator::AccountFileOperator()
49 {}
50
~AccountFileOperator()51 AccountFileOperator::~AccountFileOperator()
52 {}
53
CreateDir(const std::string & path,mode_t mode)54 ErrCode AccountFileOperator::CreateDir(const std::string &path, mode_t mode)
55 {
56 ACCOUNT_LOGI("Start creating a directory");
57 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
58 if (!OHOS::ForceCreateDirectory(path)) {
59 ACCOUNT_LOGE("failed to create %{public}s, errno %{public}d.", path.c_str(), errno);
60 return ERR_OSACCOUNT_SERVICE_FILE_CREATE_DIR_ERROR;
61 }
62 SetDirDelFlags(path);
63 bool createFlag = OHOS::ChangeModeDirectory(path, mode);
64 if (!createFlag) {
65 ACCOUNT_LOGE("failed to change mode for %{public}s, errno %{public}d.", path.c_str(), errno);
66 return ERR_OSACCOUNT_SERVICE_FILE_CHANGE_DIR_MODE_ERROR;
67 }
68
69 return ERR_OK;
70 }
71
DeleteDirOrFile(const std::string & path)72 ErrCode AccountFileOperator::DeleteDirOrFile(const std::string &path)
73 {
74 if (IsExistDir(path)) {
75 return DeleteDir(path);
76 }
77 if (IsExistFile(path)) {
78 return DeleteFile(path);
79 }
80 ACCOUNT_LOGI("Dir or file does not exist, path %{public}s.", path.c_str());
81 return ERR_OK;
82 }
83
DeleteDir(const std::string & path)84 ErrCode AccountFileOperator::DeleteDir(const std::string &path)
85 {
86 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
87 bool delFlag = false;
88 delFlag = OHOS::ForceRemoveDirectory(path);
89 if (!delFlag) {
90 ACCOUNT_LOGE("DeleteDirOrFile failed, path %{public}s errno %{public}d.", path.c_str(), errno);
91 return ERR_OSACCOUNT_SERVICE_FILE_DELE_ERROR;
92 }
93 #ifdef ENABLE_FILE_WATCHER
94 SetValidDeleteFileOperationFlag(path, true);
95 #endif // ENABLE_FILE_WATCHER
96 return ERR_OK;
97 }
98
DeleteFile(const std::string & path)99 ErrCode AccountFileOperator::DeleteFile(const std::string &path)
100 {
101 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
102 bool delFlag = false;
103 delFlag = OHOS::RemoveFile(path);
104 if (!delFlag) {
105 ACCOUNT_LOGE("DeleteDirOrFile failed, path %{public}s errno %{public}d.", path.c_str(), errno);
106 return ERR_OSACCOUNT_SERVICE_FILE_DELE_ERROR;
107 }
108 #ifdef ENABLE_FILE_WATCHER
109 SetValidDeleteFileOperationFlag(path, true);
110 #endif // ENABLE_FILE_WATCHER
111 return ERR_OK;
112 }
113
114 #ifdef ENABLE_FILE_WATCHER
SetValidModifyFileOperationFlag(const std::string & fileName,bool flag)115 void AccountFileOperator::SetValidModifyFileOperationFlag(const std::string &fileName, bool flag)
116 {
117 if (fileName.find(ACCOUNT_INFO_DIGEST_FILE_PATH) != std::string::npos) { // ignore digest file record
118 return;
119 }
120 if (!flag) {
121 validModifyFileOperationFlag_.erase(
122 std::remove(validModifyFileOperationFlag_.begin(), validModifyFileOperationFlag_.end(), fileName),
123 validModifyFileOperationFlag_.end());
124 return;
125 }
126 if (std::find(validModifyFileOperationFlag_.begin(), validModifyFileOperationFlag_.end(), fileName) ==
127 validModifyFileOperationFlag_.end()) {
128 validModifyFileOperationFlag_.emplace_back(fileName);
129 }
130 }
131
GetValidModifyFileOperationFlag(const std::string & fileName)132 bool AccountFileOperator::GetValidModifyFileOperationFlag(const std::string &fileName)
133 {
134 for (auto iter : validModifyFileOperationFlag_) {
135 if (iter == fileName) {
136 return true;
137 }
138 }
139 return false;
140 }
141
SetValidDeleteFileOperationFlag(const std::string & fileName,bool flag)142 void AccountFileOperator::SetValidDeleteFileOperationFlag(const std::string &fileName, bool flag)
143 {
144 if (!flag) {
145 validDeleteFileOperationFlag_.erase(
146 std::remove(validDeleteFileOperationFlag_.begin(), validDeleteFileOperationFlag_.end(), fileName),
147 validDeleteFileOperationFlag_.end());
148 return;
149 }
150 validDeleteFileOperationFlag_.emplace_back(fileName);
151 }
152
GetValidDeleteFileOperationFlag(const std::string & fileName)153 bool AccountFileOperator::GetValidDeleteFileOperationFlag(const std::string &fileName)
154 {
155 for (auto iter : validDeleteFileOperationFlag_) {
156 if (fileName.find(iter) != std::string::npos) {
157 return true;
158 }
159 }
160 return false;
161 }
162 #endif // ENABLE_FILE_WATCHER
163
IsDataStorageSufficient(const unsigned long long reqFreeBytes)164 static bool IsDataStorageSufficient(const unsigned long long reqFreeBytes)
165 {
166 struct statvfs diskInfo;
167 int ret = statvfs("/data", &diskInfo);
168 if (ret != 0) {
169 ACCOUNT_LOGE("Get disk info failed, ret=%{public}d, errno=%{public}d.", ret, errno);
170 return false;
171 }
172
173 unsigned long long freeBytes =
174 static_cast<unsigned long long>(diskInfo.f_bsize) * static_cast<unsigned long long>(diskInfo.f_bavail);
175 bool isSufficient = (freeBytes > reqFreeBytes + BUFF_FILE_SIZE);
176 if (!isSufficient) {
177 ACCOUNT_LOGE("Data storage is insufficient, freeBytes=%{public}llu, reqFreeBytes=%{public}llu.", freeBytes,
178 reqFreeBytes);
179 }
180 return isSufficient;
181 }
182
SetDirDelFlags(const std::string & dirpath)183 bool AccountFileOperator::SetDirDelFlags(const std::string &dirpath)
184 {
185 char realPath[PATH_MAX] = {0};
186 if (realpath(dirpath.c_str(), realPath) == nullptr) {
187 ACCOUNT_LOGE("Failed to get realpath");
188 return false;
189 }
190 int32_t fd = open(realPath, O_DIRECTORY);
191 if (fd < 0) {
192 ACCOUNT_LOGE("Failed to open dir, errno: %{public}d", errno);
193 return false;
194 }
195 unsigned int flags = 0;
196 int32_t ret = ioctl(fd, HMFS_IOCTL_HW_GET_FLAGS, &flags);
197 if (ret < 0) {
198 close(fd);
199 ACCOUNT_LOGE("Failed to get flags, errno: %{public}d", errno);
200 return false;
201 }
202 if (flags & HMFS_MONITOR_FL) {
203 close(fd);
204 ACCOUNT_LOGE("Delete control flag is already set");
205 return false;
206 }
207 flags |= HMFS_MONITOR_FL;
208 ret = ioctl(fd, HMFS_IOCTL_HW_SET_FLAGS, &flags);
209 if (ret < 0) {
210 close(fd);
211 ACCOUNT_LOGE("Failed to set flags, errno: %{public}d", errno);
212 return false;
213 }
214 close(fd);
215 return true;
216 }
217
WriteFile(const std::string & path,const std::string & content)218 ErrCode AccountFileOperator::WriteFile(const std::string &path, const std::string &content)
219 {
220 std::unique_lock<std::shared_timed_mutex> lock(fileLock_);
221 #ifdef ENABLE_FILE_WATCHER
222 SetValidModifyFileOperationFlag(path, true);
223 #endif // ENABLE_FILE_WATCHER
224 FILE *fp = fopen(path.c_str(), "wb");
225 if (fp == nullptr) {
226 ACCOUNT_LOGE("failed to open %{public}s, errno %{public}d.", path.c_str(), errno);
227 #ifdef ENABLE_FILE_WATCHER
228 SetValidModifyFileOperationFlag(path, false);
229 #endif // ENABLE_FILE_WATCHER
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 (void)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 (void)fclose(fp);
259 #ifdef ENABLE_FILE_WATCHER
260 SetValidModifyFileOperationFlag(path, false);
261 #endif // ENABLE_FILE_WATCHER
262 return ERR_ACCOUNT_COMMON_FILE_WRITE_FAILED;
263 }
264
InputFileByPathAndContent(const std::string & path,const std::string & content)265 ErrCode AccountFileOperator::InputFileByPathAndContent(const std::string &path, const std::string &content)
266 {
267 std::string str = path;
268 str.erase(str.rfind('/'));
269 if (!IsExistDir(str)) {
270 ErrCode errCode = CreateDir(str);
271 if (errCode != ERR_OK) {
272 ACCOUNT_LOGE("failed to create dir, str = %{public}s errCode %{public}d.", str.c_str(), errCode);
273 return errCode;
274 }
275 }
276 if (!IsDataStorageSufficient(content.length())) {
277 return ERR_ACCOUNT_COMMON_DATA_NO_SPACE;
278 }
279 return WriteFile(path, content);
280 }
281
GetFileContentByPath(const std::string & path,std::string & content)282 ErrCode AccountFileOperator::GetFileContentByPath(const std::string &path, std::string &content)
283 {
284 if (!IsExistFile(path)) {
285 ACCOUNT_LOGE("cannot find file, path = %{public}s", path.c_str());
286 return ERR_OSACCOUNT_SERVICE_FILE_FIND_FILE_ERROR;
287 }
288 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
289 FILE *fp = fopen(path.c_str(), "rb");
290 if (fp == nullptr) {
291 ACCOUNT_LOGE("cannot open file %{public}s, errno %{public}d.", path.c_str(), errno);
292 return ERR_ACCOUNT_COMMON_FILE_OPEN_FAILED;
293 }
294 int fd = fileno(fp);
295 flock(fd, LOCK_SH);
296 (void) fseek(fp, 0, SEEK_END);
297 long fileSize = ftell(fp);
298 if ((fileSize < 0) || (fileSize > MAX_FILE_SIZE)) {
299 ACCOUNT_LOGE("the file(%{public}s) size is invalid, errno %{public}d.", path.c_str(), errno);
300 flock(fd, LOCK_UN);
301 (void) fclose(fp);
302 return ERR_ACCOUNT_COMMON_FILE_READ_FAILED;
303 }
304 rewind(fp);
305 char *buffer = new (std::nothrow) char[fileSize];
306 if (buffer == nullptr) {
307 ACCOUNT_LOGE("insufficient memory");
308 flock(fd, LOCK_UN);
309 (void) fclose(fp);
310 return ERR_ACCOUNT_COMMON_INSUFFICIENT_MEMORY_ERROR;
311 }
312 size_t retSize = fread(buffer, sizeof(char), fileSize, fp);
313 if (static_cast<long>(retSize) != fileSize) {
314 ACCOUNT_LOGE("fail to read file %{public}s", path.c_str());
315 delete[] buffer;
316 flock(fd, LOCK_UN);
317 (void) fclose(fp);
318 return ERR_ACCOUNT_COMMON_FILE_READ_FAILED;
319 }
320 content = std::string(buffer, retSize);
321 delete[] buffer;
322 flock(fd, LOCK_UN);
323 (void) fclose(fp);
324 return ERR_OK;
325 }
326
IsExistFile(const std::string & path)327 bool AccountFileOperator::IsExistFile(const std::string &path)
328 {
329 if (path.empty()) {
330 ACCOUNT_LOGE("Path is empty.");
331 return false;
332 }
333 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
334 uint32_t retryCount = 0;
335 while (retryCount < RETRY_TIMES) {
336 struct stat buf = {};
337 if (stat(path.c_str(), &buf) == 0) {
338 return S_ISREG(buf.st_mode);
339 }
340 if (errno != ENOENT) {
341 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d. Retrying...", path.c_str(), errno);
342 std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_SLEEP_MS));
343 retryCount++;
344 } else {
345 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d.", path.c_str(), errno);
346 return false;
347 }
348 }
349 return false;
350 }
351
CheckFileExistence(const std::string & path)352 ErrCode AccountFileOperator::CheckFileExistence(const std::string &path)
353 {
354 if (path.empty()) {
355 ACCOUNT_LOGE("Path is empty.");
356 return ERR_ACCOUNT_COMMON_INVALID_PARAMETER;
357 }
358 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
359 uint32_t retryCount = 0;
360 while (retryCount < RETRY_TIMES) {
361 struct stat buf = {};
362 if (stat(path.c_str(), &buf) == 0) {
363 if (S_ISREG(buf.st_mode)) {
364 return ERR_OK;
365 }
366 ACCOUNT_LOGE("S_ISREG failed, errno=%{public}d.", errno);
367 return ERR_ACCOUNT_COMMON_FILE_OTHER_ERROR;
368 }
369 if (errno != ENOENT) {
370 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d. Retrying...", path.c_str(), errno);
371 std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_SLEEP_MS));
372 retryCount++;
373 } else {
374 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d.", path.c_str(), errno);
375 return ERR_ACCOUNT_COMMON_FILE_NOT_EXIST;
376 }
377 }
378 return ERR_ACCOUNT_COMMON_FILE_OTHER_ERROR;
379 }
380
IsJsonFormat(const std::string & path)381 bool AccountFileOperator::IsJsonFormat(const std::string &path)
382 {
383 std::string content;
384 if (GetFileContentByPath(path, content) != ERR_OK) {
385 return false;
386 }
387
388 nlohmann::json jsonData = nlohmann::json::parse(content, nullptr, false);
389 if (jsonData.is_discarded() || !jsonData.is_structured()) {
390 ACCOUNT_LOGE("File %{public}s is invalid json format, size: %{public}zu", path.c_str(), content.size());
391 return false;
392 }
393 return true;
394 }
395
IsJsonFileReady(const std::string & path)396 bool AccountFileOperator::IsJsonFileReady(const std::string &path)
397 {
398 return IsExistFile(path) && IsJsonFormat(path);
399 }
400
IsExistDir(const std::string & path)401 bool AccountFileOperator::IsExistDir(const std::string &path)
402 {
403 if (path.empty()) {
404 return false;
405 }
406 std::shared_lock<std::shared_timed_mutex> lock(fileLock_);
407 struct stat buf = {};
408 if (stat(path.c_str(), &buf) != 0) {
409 ACCOUNT_LOGE("Stat %{public}s failed, errno=%{public}d", path.c_str(), errno);
410 return false;
411 }
412
413 return S_ISDIR(buf.st_mode);
414 }
415 } // namespace AccountSA
416 } // namespace OHOS
417