• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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