• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
16 #include "preferences_helper.h"
17 
18 #include <cerrno>
19 #include <climits>
20 #include <cstdlib>
21 #include <utility>
22 
23 #include "log_print.h"
24 #include "preferences.h"
25 #include "preferences_errno.h"
26 #include "preferences_file_lock.h"
27 #include "preferences_file_operation.h"
28 #include "preferences_dfx_adapter.h"
29 #include "preferences_impl.h"
30 #include "preferences_enhance_impl.h"
31 #include "preferences_utils.h"
32 
33 namespace OHOS {
34 namespace NativePreferences {
35 std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>> PreferencesHelper::prefsCache_;
36 std::mutex PreferencesHelper::prefsCacheMutex_;
37 std::atomic<bool> PreferencesHelper::isReportFault_(false);
38 static constexpr const int DB_SUFFIX_NUM = 6;
39 static constexpr const char *DB_SUFFIX[DB_SUFFIX_NUM] = { ".ctrl", ".ctrl.dwr", ".redo", ".undo", ".safe", ".map" };
40 
IsFileExist(const std::string & path)41 static bool IsFileExist(const std::string &path)
42 {
43     struct stat buffer;
44     return (stat(path.c_str(), &buffer) == 0);
45 }
46 
RemoveEnhanceDb(const std::string & filePath)47 static int RemoveEnhanceDb(const std::string &filePath)
48 {
49     if (std::remove(filePath.c_str()) != 0) {
50         LOG_ERROR("remove %{public}s failed.", ExtractFileName(filePath).c_str());
51         return E_DELETE_FILE_FAIL;
52     }
53     return E_OK;
54 }
55 
RemoveEnhanceDbFileIfNeed(const std::string & filePath)56 static int RemoveEnhanceDbFileIfNeed(const std::string &filePath)
57 {
58     std::string dbFilePath = filePath + ".db";
59     if (IsFileExist(dbFilePath) && RemoveEnhanceDb(dbFilePath) != E_OK) {
60         LOG_ERROR("remove dbFilePath failed.");
61         return E_DELETE_FILE_FAIL;
62     }
63     for (int index = 0; index < DB_SUFFIX_NUM; index++) {
64         std::string tmpFilePath = dbFilePath + DB_SUFFIX[index];
65         if (IsFileExist(tmpFilePath) && RemoveEnhanceDb(tmpFilePath) != E_OK) {
66             return E_DELETE_FILE_FAIL;
67         }
68     }
69     LOG_DEBUG("db files has been removed.");
70     return E_OK;
71 }
72 
GetRealPath(const std::string & path,int & errorCode)73 std::string PreferencesHelper::GetRealPath(const std::string &path, int &errorCode)
74 {
75     if (path.empty()) {
76         LOG_ERROR("The path can not be empty.");
77         errorCode = E_EMPTY_FILE_PATH;
78         return "";
79     }
80     if (path.length() > PATH_MAX) {
81         LOG_ERROR("The path exceeds max length.");
82         errorCode = E_PATH_EXCEED_MAX_LENGTH;
83         return "";
84     }
85     std::string::size_type pos = path.find_last_of('/');
86     if (pos == std::string::npos) {
87         LOG_ERROR("path can not be relative path.");
88         errorCode = E_RELATIVE_PATH;
89         return "";
90     }
91 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
92     if (path.at(1) != ':') {
93         LOG_ERROR("The path can not be relative path.");
94         errorCode = E_RELATIVE_PATH;
95         return "";
96     }
97     std::string filePath = path.substr(0, pos);
98     if (Access(filePath) != 0 && !Mkdir(filePath)) {
99         LOG_ERROR("Failed to create path");
100         errorCode = E_INVALID_FILE_PATH;
101         return "";
102     }
103 #else
104     if (path.front() != '/') {
105         LOG_ERROR("The path can not be relative path.");
106         errorCode = E_RELATIVE_PATH;
107         return "";
108     }
109 #endif
110     std::string fileName = path.substr(pos + 1, path.length());
111     if (fileName.empty()) {
112         LOG_ERROR("file name can not be empty.");
113         errorCode = E_EMPTY_FILE_NAME;
114         return "";
115     }
116     errorCode = E_OK;
117     return path;
118 }
119 
IsInTrustList(const std::string & bundleName)120 static bool IsInTrustList(const std::string &bundleName)
121 {
122     std::vector<std::string> trustList = {"uttest", "alipay", "com.jd.", "cmblife", "os.mms", "os.ouc",
123         "meetimeservice"};
124     for (size_t i = 0; i < trustList.size(); i++) {
125         if (bundleName.find(trustList[i]) != std::string::npos) {
126             return true;
127         }
128     }
129     return false;
130 }
131 
GetPreferencesInner(const Options & options,bool & isEnhancePreferences,std::shared_ptr<Preferences> & pref)132 int PreferencesHelper::GetPreferencesInner(const Options &options, bool &isEnhancePreferences,
133     std::shared_ptr<Preferences> &pref)
134 {
135     if (IsInTrustList(options.bundleName)) {
136         if (!IsFileExist(options.filePath) && IsStorageTypeSupported(StorageType::GSKV)) {
137             pref = PreferencesEnhanceImpl::GetPreferences(options);
138             isEnhancePreferences = true;
139             return std::static_pointer_cast<PreferencesEnhanceImpl>(pref)->Init();
140         }
141         pref = PreferencesImpl::GetPreferences(options);
142         isEnhancePreferences = false;
143         return std::static_pointer_cast<PreferencesImpl>(pref)->Init();
144     }
145     if (!options.isEnhance) {
146         // xml
147         if (IsFileExist(options.filePath + ".db")) {
148             LOG_ERROR("GSKV exists, failed to get preferences by XML.");
149             return E_NOT_SUPPORTED;
150         }
151         pref = PreferencesImpl::GetPreferences(options);
152         isEnhancePreferences = false;
153         return std::static_pointer_cast<PreferencesImpl>(pref)->Init();
154     }
155     // GSKV
156     if (IsFileExist(options.filePath)) {
157         LOG_ERROR("XML exists, failed to get preferences by GSKV.");
158         return E_NOT_SUPPORTED;
159     }
160     if (!IsStorageTypeSupported(StorageType::GSKV)) {
161         // log inside
162         return E_NOT_SUPPORTED;
163     }
164     pref = PreferencesEnhanceImpl::GetPreferences(options);
165     isEnhancePreferences = true;
166     return std::static_pointer_cast<PreferencesEnhanceImpl>(pref)->Init();
167 }
168 
GetPreferences(const Options & options,int & errCode)169 std::shared_ptr<Preferences> PreferencesHelper::GetPreferences(const Options &options, int &errCode)
170 {
171     std::string realPath = GetRealPath(options.filePath, errCode);
172     if (realPath == "" || errCode != E_OK) {
173         return nullptr;
174     }
175 
176     std::lock_guard<std::mutex> lock(prefsCacheMutex_);
177     auto it = prefsCache_.find(realPath);
178     if (it != prefsCache_.end()) {
179         auto pre = it->second.first;
180         if (pre != nullptr) {
181             LOG_DEBUG("GetPreferences: found preferences in cache");
182             return pre;
183         }
184         LOG_DEBUG("GetPreferences: found preferences in cache but it's null, erase it.");
185         prefsCache_.erase(it);
186     }
187 
188     const_cast<Options &>(options).filePath = realPath;
189     std::string::size_type pos = realPath.find_last_of('/');
190     std::string filePath = realPath.substr(0, pos);
191     if (Access(filePath.c_str()) != 0) {
192         LOG_ERROR("The path is invalid, prefName is %{public}s.", ExtractFileName(filePath).c_str());
193         if (!PreferencesHelper::isReportFault_.exchange(true)) {
194             ReportFaultParam param = { "GetPreferences error", options.bundleName, NORMAL_DB,
195                 ExtractFileName(options.filePath), E_INVALID_FILE_PATH, "The path is invalid." };
196             PreferencesDfxManager::ReportFault(param);
197         }
198     }
199     bool isEnhancePreferences = false;
200     std::shared_ptr<Preferences> pref = nullptr;
201     errCode = GetPreferencesInner(options, isEnhancePreferences, pref);
202     if (errCode != E_OK) {
203         return nullptr;
204     }
205     prefsCache_.insert({realPath, {pref, isEnhancePreferences}});
206     return pref;
207 }
208 
DeletePreferencesCache(const std::string & realPath)209 std::pair<std::string, int> PreferencesHelper::DeletePreferencesCache(const std::string &realPath)
210 {
211     std::string bundleName;
212     int errCode = E_OK;
213     std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>>::iterator it = prefsCache_.find(realPath);
214     if (it != prefsCache_.end()) {
215         auto pref = it->second.first;
216         if (pref != nullptr) {
217             LOG_INFO("Begin to Delete Preferences: %{public}s", ExtractFileName(realPath).c_str());
218             bundleName = pref->GetBundleName();
219             if (it->second.second) {
220                 errCode = pref->CloseDb();
221             } else {
222                 errCode = std::static_pointer_cast<PreferencesImpl>(pref)->Close();
223             }
224         }
225         if (errCode == E_OK) {
226             prefsCache_.erase(it);
227         }
228     }
229 
230     return { bundleName, errCode };
231 }
232 
DeletePreferences(const std::string & path)233 int PreferencesHelper::DeletePreferences(const std::string &path)
234 {
235     int errCode = E_OK;
236     std::string realPath = GetRealPath(path, errCode);
237     if (realPath == "" || errCode != E_OK) {
238         return errCode;
239     }
240 
241     std::string bundleName;
242     {
243         std::lock_guard<std::mutex> lock(prefsCacheMutex_);
244         auto [ name, code ] = DeletePreferencesCache(realPath);
245         if (code != E_OK) {
246             LOG_ERROR("file %{public}s failed to close when delete preferences, errCode is: %{public}d",
247                 ExtractFileName(realPath).c_str(), code);
248             return code;
249         }
250         bundleName = name;
251     }
252 
253     std::string filePath = realPath.c_str();
254     std::string backupPath = MakeFilePath(filePath, STR_BACKUP);
255     std::string brokenPath = MakeFilePath(filePath, STR_BROKEN);
256     std::string lockFilePath = MakeFilePath(filePath, STR_LOCK);
257 
258     bool isMultiProcessing = false;
259     PreferencesFileLock fileLock(filePath);
260     fileLock.WriteLock(isMultiProcessing);
261     if (isMultiProcessing) {
262         LOG_ERROR("The file has cross-process operations, fileName is %{public}s.", ExtractFileName(filePath).c_str());
263         ReportFaultParam param = { "DeletePreferences error", bundleName, NORMAL_DB, ExtractFileName(filePath),
264             E_OPERAT_IS_CROSS_PROESS, "Cross-process operations." };
265         PreferencesDfxManager::ReportFault(param);
266     }
267     std::remove(filePath.c_str());
268     std::remove(backupPath.c_str());
269     std::remove(brokenPath.c_str());
270     std::remove(lockFilePath.c_str());
271     if (RemoveEnhanceDbFileIfNeed(path) != E_OK) {
272         return E_DELETE_FILE_FAIL;
273     }
274 
275     if (IsFileExist(filePath) || IsFileExist(backupPath) || IsFileExist(brokenPath) || IsFileExist(lockFilePath)) {
276         return E_DELETE_FILE_FAIL;
277     }
278     return E_OK;
279 }
280 
RemovePreferencesFromCache(const std::string & path)281 int PreferencesHelper::RemovePreferencesFromCache(const std::string &path)
282 {
283     int errCode = E_OK;
284     std::string realPath = GetRealPath(path, errCode);
285     if (realPath == "" || errCode != E_OK) {
286         return errCode;
287     }
288 
289     std::lock_guard<std::mutex> lock(prefsCacheMutex_);
290     std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>>::iterator it = prefsCache_.find(realPath);
291     if (it == prefsCache_.end()) {
292         LOG_DEBUG("RemovePreferencesFromCache: preferences not in cache, just return");
293         return E_OK;
294     }
295 
296     auto pref = it->second.first;
297     if (pref != nullptr) {
298         if (it->second.second) {
299             errCode = std::static_pointer_cast<PreferencesEnhanceImpl>(pref)->CloseDb();
300             if (errCode != E_OK) {
301                 LOG_ERROR("RemovePreferencesFromCache: file %{public}s failed to close db, errCode is %{public}d.",
302                     ExtractFileName(realPath).c_str(), errCode);
303                 return E_ERROR;
304             }
305         } else {
306             std::static_pointer_cast<PreferencesImpl>(pref)->Close();
307         }
308     }
309 
310     prefsCache_.erase(it);
311     return E_OK;
312 }
313 
IsStorageTypeSupported(const StorageType & type)314 bool PreferencesHelper::IsStorageTypeSupported(const StorageType &type)
315 {
316     if (type == StorageType::XML) {
317         return true;
318     }
319     if (type == StorageType::GSKV) {
320 #if !defined(CROSS_PLATFORM) && defined(ARKDATA_DATABASE_CORE_ENABLE)
321         return true;
322 #else
323         LOG_WARN("GSKV not support this platform.");
324         return false;
325 #endif
326     }
327     return false;
328 }
329 } // End of namespace NativePreferences
330 } // End of namespace OHOS