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_db_adapter.h"
26 #include "preferences_errno.h"
27 #include "preferences_file_lock.h"
28 #include "preferences_file_operation.h"
29 #include "preferences_dfx_adapter.h"
30 #include "preferences_impl.h"
31 #include "preferences_enhance_impl.h"
32 namespace OHOS {
33 namespace NativePreferences {
34 std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>> PreferencesHelper::prefsCache_;
35 std::mutex PreferencesHelper::prefsCacheMutex_;
36 static constexpr const int DB_SUFFIX_NUM = 6;
37 static constexpr const char *DB_SUFFIX[DB_SUFFIX_NUM] = { ".ctrl", ".ctrl.dwr", ".redo", ".undo", ".safe", ".map" };
38 
IsFileExist(const std::string &path)39 static bool IsFileExist(const std::string &path)
40 {
41     struct stat buffer;
42     return (stat(path.c_str(), &buffer) == 0);
43 }
44 
RemoveEnhanceDb(const std::string &filePath)45 static int RemoveEnhanceDb(const std::string &filePath)
46 {
47     if (std::remove(filePath.c_str()) != 0) {
48         LOG_ERROR("remove %{public}s failed.", ExtractFileName(filePath).c_str());
49         return E_DELETE_FILE_FAIL;
50     }
51     return E_OK;
52 }
53 
RemoveEnhanceDbFileIfNeed(const std::string &filePath)54 static int RemoveEnhanceDbFileIfNeed(const std::string &filePath)
55 {
56     std::string dbFilePath = filePath + ".db";
57     if (IsFileExist(dbFilePath) && RemoveEnhanceDb(dbFilePath) != E_OK) {
58         LOG_ERROR("remove dbFilePath failed.");
59         return E_DELETE_FILE_FAIL;
60     }
61     for (int index = 0; index < DB_SUFFIX_NUM; index++) {
62         std::string tmpFilePath = dbFilePath + DB_SUFFIX[index];
63         if (IsFileExist(tmpFilePath) && RemoveEnhanceDb(tmpFilePath) != E_OK) {
64             return E_DELETE_FILE_FAIL;
65         }
66     }
67     LOG_DEBUG("db files has been removed.");
68     return E_OK;
69 }
70 
GetRealPath(const std::string &path, int &errorCode)71 std::string PreferencesHelper::GetRealPath(const std::string &path, int &errorCode)
72 {
73     if (path.empty()) {
74         LOG_ERROR("The path can not be empty.");
75         errorCode = E_EMPTY_FILE_PATH;
76         return "";
77     }
78     if (path.length() > PATH_MAX) {
79         LOG_ERROR("The path exceeds max length.");
80         errorCode = E_PATH_EXCEED_MAX_LENGTH;
81         return "";
82     }
83     std::string::size_type pos = path.find_last_of('/');
84     if (pos == std::string::npos) {
85         LOG_ERROR("path can not be relative path.");
86         errorCode = E_RELATIVE_PATH;
87         return "";
88     }
89 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
90     if (path.at(1) != ':') {
91         LOG_ERROR("The path can not be relative path.");
92         errorCode = E_RELATIVE_PATH;
93         return "";
94     }
95     std::string filePath = path.substr(0, pos);
96     if (Access(filePath) != 0 && !Mkdir(filePath)) {
97         LOG_ERROR("Failed to create path");
98         errorCode = E_INVALID_FILE_PATH;
99         return "";
100     }
101 #else
102     if (path.front() != '/') {
103         LOG_ERROR("The path can not be relative path.");
104         errorCode = E_RELATIVE_PATH;
105         return "";
106     }
107 #endif
108     std::string fileName = path.substr(pos + 1, path.length());
109     if (fileName.empty()) {
110         LOG_ERROR("file name can not be empty.");
111         errorCode = E_EMPTY_FILE_NAME;
112         return "";
113     }
114     errorCode = E_OK;
115     return path;
116 }
117 
118 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
IsUseEnhanceDb(const Options &options)119 static bool IsUseEnhanceDb(const Options &options)
120 {
121     if (IsFileExist(options.filePath)) {
122         return false;
123     }
124     bool bundleCheck = (options.bundleName.find("uttest") != std::string::npos ||
125         options.bundleName.find("alipay") != std::string::npos ||
126         options.bundleName.find("com.jd.") != std::string::npos ||
127         options.bundleName.find("cmblife") != std::string::npos ||
128         options.bundleName.find("os.mms") != std::string::npos ||
129         options.bundleName.find("os.ouc") != std::string::npos ||
130         options.bundleName.find("meetimeservice") != std::string::npos);
131     if (!options.isEnhance && !bundleCheck) {
132         return false;
133     }
134     PreferenceDbAdapter::ApiInit();
135     return PreferenceDbAdapter::IsEnhandceDbEnable();
136 }
137 #endif
138 
GetPreferences(const Options &options, int &errCode)139 std::shared_ptr<Preferences> PreferencesHelper::GetPreferences(const Options &options, int &errCode)
140 {
141     std::string realPath = GetRealPath(options.filePath, errCode);
142     if (realPath == "" || errCode != E_OK) {
143         return nullptr;
144     }
145 
146     std::lock_guard<std::mutex> lock(prefsCacheMutex_);
147     auto it = prefsCache_.find(realPath);
148     if (it != prefsCache_.end()) {
149         auto pre = it->second.first;
150         if (pre != nullptr) {
151             LOG_DEBUG("GetPreferences: found preferences in cache");
152             return pre;
153         }
154         LOG_DEBUG("GetPreferences: found preferences in cache but it's null, erase it.");
155         prefsCache_.erase(it);
156     }
157 
158     const_cast<Options &>(options).filePath = realPath;
159     std::string::size_type pos = realPath.find_last_of('/');
160     std::string filePath = realPath.substr(0, pos);
161     if (access(filePath.c_str(), F_OK) != 0) {
162         LOG_ERROR("The path is invalid, prefName is %{public}s.", ExtractFileName(realPath).c_str());
163         errCode = E_INVALID_FILE_PATH;
164         return nullptr;
165     }
166     std::shared_ptr<Preferences> pref = nullptr;
167     bool isEnhancePreferences = false;
168 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(ANDROID_PLATFORM) &&!defined(IOS_PLATFORM)
169     if (IsUseEnhanceDb(options)) {
170         LOG_DEBUG("PreferencesHelper::GetPreferences using enhance db.");
171         pref = PreferencesEnhanceImpl::GetPreferences(options);
172         errCode = std::static_pointer_cast<PreferencesEnhanceImpl>(pref)->Init();
173         isEnhancePreferences = true;
174     } else {
175         pref = PreferencesImpl::GetPreferences(options);
176         errCode = std::static_pointer_cast<PreferencesImpl>(pref)->Init();
177     }
178 #else
179     pref = PreferencesImpl::GetPreferences(options);
180     errCode = std::static_pointer_cast<PreferencesImpl>(pref)->Init();
181 #endif
182     if (errCode != E_OK) {
183         return nullptr;
184     }
185     prefsCache_.insert({realPath, {pref, isEnhancePreferences}});
186     return pref;
187 }
188 
DeletePreferences(const std::string &path)189 int PreferencesHelper::DeletePreferences(const std::string &path)
190 {
191     int errCode = E_OK;
192     std::string realPath = GetRealPath(path, errCode);
193     if (realPath == "" || errCode != E_OK) {
194         return errCode;
195     }
196 
197     std::string dataGroupId = "";
198     {
199         std::lock_guard<std::mutex> lock(prefsCacheMutex_);
200         std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>>::iterator it = prefsCache_.find(realPath);
201         if (it != prefsCache_.end()) {
202             auto pref = it->second.first;
203             if (pref != nullptr) {
204                 LOG_INFO("Begin to Delete Preferences: %{public}s", ExtractFileName(path).c_str());
205                 dataGroupId = pref->GetGroupId();
206                 errCode = pref->CloseDb();
207                 if (errCode != E_OK) {
208                     LOG_ERROR("failed to close db when delete preferences.");
209                     return errCode;
210                 }
211             }
212             pref = nullptr;
213             prefsCache_.erase(it);
214             LOG_DEBUG("DeletePreferences: found preferences in cache, erase it.");
215         } else {
216             LOG_DEBUG("DeletePreferences: cache not found, just delete files.");
217         }
218     }
219 
220     std::string filePath = realPath.c_str();
221     std::string backupPath = MakeFilePath(filePath, STR_BACKUP);
222     std::string brokenPath = MakeFilePath(filePath, STR_BROKEN);
223     std::string lockFilePath = MakeFilePath(filePath, STR_LOCK);
224 
225     PreferencesFileLock fileLock(lockFilePath, dataGroupId);
226     std::remove(filePath.c_str());
227     std::remove(backupPath.c_str());
228     std::remove(brokenPath.c_str());
229     if (RemoveEnhanceDbFileIfNeed(path) != E_OK) {
230         return E_DELETE_FILE_FAIL;
231     }
232 
233     if (!dataGroupId.empty()) {
234         std::remove(lockFilePath.c_str());
235     }
236 
237     if (IsFileExist(filePath) || IsFileExist(backupPath) || IsFileExist(brokenPath)) {
238         return E_DELETE_FILE_FAIL;
239     }
240     return E_OK;
241 }
242 
RemovePreferencesFromCache(const std::string &path)243 int PreferencesHelper::RemovePreferencesFromCache(const std::string &path)
244 {
245     int errCode = E_OK;
246     std::string realPath = GetRealPath(path, errCode);
247     if (realPath == "" || errCode != E_OK) {
248         return errCode;
249     }
250 
251     std::lock_guard<std::mutex> lock(prefsCacheMutex_);
252     std::map<std::string, std::pair<std::shared_ptr<Preferences>, bool>>::iterator it = prefsCache_.find(realPath);
253     if (it == prefsCache_.end()) {
254         LOG_DEBUG("RemovePreferencesFromCache: preferences not in cache, just return");
255         return E_OK;
256     }
257 
258     if (it->second.second) {
259         auto pref = it->second.first;
260         errCode = std::static_pointer_cast<PreferencesEnhanceImpl>(pref)->CloseDb();
261         if (errCode != E_OK) {
262             LOG_ERROR("RemovePreferencesFromCache: failed to close db.");
263             return E_ERROR;
264         }
265     }
266 
267     prefsCache_.erase(it);
268     return E_OK;
269 }
270 } // End of namespace NativePreferences
271 } // End of namespace OHOS