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