• 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 #define LOG_TAG "SqliteUtils"
16 #include "sqlite_utils.h"
17 
18 #include <fcntl.h>
19 #include <sqlite3sym.h>
20 #include <sys/file.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <algorithm>
26 #include <cerrno>
27 #include <climits>
28 #include <cstddef>
29 #include <cstdio>
30 #include <cstring>
31 #if !defined(CROSS_PLATFORM)
32 #include <filesystem>
33 #include <sqlite3.h>
34 #include "relational/relational_store_sqlite_ext.h"
35 #endif
36 #include <fstream>
37 #include <regex>
38 #include <string>
39 #include <sstream>
40 #include <iomanip>
41 
42 #include "logger.h"
43 #include "rdb_errno.h"
44 #include "rdb_store_config.h"
45 #include "string_utils.h"
46 #include "rdb_time_utils.h"
47 
48 namespace OHOS {
49 namespace NativeRdb {
50 using namespace OHOS::Rdb;
51 /* A continuous number must contain at least eight digits, because the employee ID has eight digits,
52     and the mobile phone number has 11 digits. The UUID is longer */
53 constexpr int32_t CONTINUOUS_DIGITS_MINI_SIZE = 6;
54 constexpr int32_t FILE_PATH_MINI_SIZE = 6;
55 constexpr int32_t AREA_MINI_SIZE = 4;
56 constexpr int32_t AREA_OFFSET_SIZE = 5;
57 constexpr int32_t PRE_OFFSET_SIZE = 1;
58 constexpr int32_t DISPLAY_BYTE = 2;
59 constexpr int32_t PREFIX_LENGTH = 3;
60 constexpr int32_t FILE_MAX_SIZE = 20 * 1024;
61 
62 constexpr int32_t HEAD_SIZE = 3;
63 constexpr int32_t END_SIZE = 3;
64 constexpr int32_t MIN_SIZE = HEAD_SIZE + END_SIZE + 3;
65 constexpr const char *REPLACE_CHAIN = "***";
66 constexpr const unsigned char MAX_PRINTABLE_BYTE = 0x7F;
67 
68 constexpr SqliteUtils::SqlType SqliteUtils::SQL_TYPE_MAP[];
69 constexpr const char *SqliteUtils::ON_CONFLICT_CLAUSE[];
70 
71 constexpr const char *SQL_KEYWORD[] = { "ABORT", "ABS", "ACTION", "ADD", "AFTER", "ALIAS", "ALL", "ALTER", "ALWAYS",
72     "AMBIGUOUS", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIG",
73     "BIGINT", "BLOB", "BOOLEAN", "BY", "CASCADE", "CASE", "CAST", "CEIL", "CEILING", "CHARACTER", "CHECK", "CLOB",
74     "COALESCE", "COLLATE", "COLUMN", "COMMIT", "CONCAT", "CONFLICT", "CONSTRAINT", "COUNT", "CREATE", "CROSS",
75     "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DATE", "DATETIME", "DECIMAL",
76     "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DIGIT", "DISTINCT", "DO", "DOUBLE", "DROP", "E",
77     "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS", "EXP", "EXPLAIN", "EXPR", "FAIL",
78     "FALSE", "FILENAME", "FILTER", "FIRST", "FLOAT", "FLOOR", "FOLLOWING", "FOR", "FOREIGN", "FROM", "FULL",
79     "GENERATED", "GLOB", "GROUP", "GROUPS", "GROUP_CONCAT", "HAVING", "HEXDIGIT", "IF", "IFNULL", "IGNORE", "IMMEDIATE",
80     "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INSTR", "INT", "INT2", "INT8", "INTEGER",
81     "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "JULIANDAY", "KEY", "LAST", "LEFT", "LENGTH", "LIKE", "LIMIT", "LN",
82     "LOG", "LOWER", "LTRIM", "MATCH", "MATERIALIZED", "MAX", "MEDIUMINT", "MIN", "NAME", "NATIVE", "NATURAL", "NCHAR",
83     "NEWLINE", "NO", "NOT", "NOTHING", "NOTNULL", "NULL", "NULLIF", "NULLS", "NUMERIC", "NVARCHAR", "OF", "OFFSET",
84     "ON", "OR", "ORDER", "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN", "POWER", "PRAGMA", "PRECEDING", "PRECISION",
85     "PRIMARY", "QUERY", "RAISE", "RANDOM", "RANGE", "REAL", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE",
86     "RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT", "ROLLBACK", "ROUND", "ROW", "ROWID", "ROWS", "RTRIM",
87     "SAVEPOINT", "SELECT", "SET", "SMALLINT", "SQRT", "STORED", "STRFTIME", "STRICT", "SUBSTR", "SUM", "TABLE", "TEMP",
88     "TEMPORARY", "TEXT", "THEN", "TIES", "TIME", "TINYINT", "TO", "TOTAL", "TRANSACTION", "TRIGGER", "TRIM", "TRUE",
89     "TYPEOF", "UNBOUNDED", "UNION", "UNIQUE", "UNSIGNED", "UPDATE", "UPPER", "USING", "VACUUM", "VALUES", "VARCHAR",
90     "VARYING", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT" };
91 
92 constexpr const char *WHILE_KEYWORDS[] = { "ABORTS", "ACQLOCK", "ALREADY", "AT", "BUSYLINE", "CHANGED", "CURLOCK",
93     "DBREF", "DEL", "DUPLICATE", "ENABLE", "ERRNO", "ERROR", "FAILED", "FD", "FILE", "FILELOCK", "FRAMES", "F_RDLCK",
94     "F_WRLCK", "GO", "HANDLELOCKS", "HAS", "IDX", "INCOMPLETE", "INPUT", "LEN", "LINE", "LITERAL", "LOCKCNT", "LOCKS",
95     "MISUSE", "MONITOR", "NEAR", "NONE", "PID", "PROCESSLOCK", "QUOTED", "READ", "RECOVERED", "SCHEMA", "SHARED_FIRST",
96     "SQLITE", "STATEMENT", "STRING", "SUCH", "SYNTAX", "TID", "TRX", "TYPE", "WAL", "WAL_DMS", "WARNING", "WRITE",
97     "WRONG" };
98 
WordCompare(const char * a,const char * b)99 constexpr int32_t WordCompare(const char *a, const char *b)
100 {
101     while (*a && *b && (*a == *b)) {
102         ++a;
103         ++b;
104     }
105     return static_cast<unsigned char>(*a) - static_cast<unsigned char>(*b);
106 }
107 
IsLexSorted(const char * const * keyword,size_t size)108 constexpr bool IsLexSorted(const char *const *keyword, size_t size)
109 {
110     for (size_t i = 1; i < size; ++i) {
111         if (WordCompare(keyword[i - 1], keyword[i]) >= 0) {
112             return false;
113         }
114     }
115     return true;
116 }
117 
IsMatchKeyword(const char * const * keyword,size_t size,const char * str)118 bool IsMatchKeyword(const char *const *keyword, size_t size, const char *str)
119 {
120     auto it = std::lower_bound(
121         keyword, keyword + size, str, [](const char *a, const char *b) { return WordCompare(a, b) < 0; });
122     if (it != keyword + size && WordCompare(*it, str) == 0) {
123         return true;
124     }
125     return false;
126 }
127 
128 // ensure lexicographical order
129 static_assert(IsLexSorted(SQL_KEYWORD, sizeof(SQL_KEYWORD) / sizeof(char*)));
130 static_assert(IsLexSorted(WHILE_KEYWORDS, sizeof(WHILE_KEYWORDS) / sizeof(char*)));
131 
GetSqlStatementType(const std::string & sql)132 int SqliteUtils::GetSqlStatementType(const std::string &sql)
133 {
134     /* the sql string length less than 3 can not be any type sql */
135     auto alnum = std::find_if(sql.begin(), sql.end(), [](int ch) { return !std::isspace(ch) && !std::iscntrl(ch); });
136     if (alnum == sql.end()) {
137         return STATEMENT_ERROR;
138     }
139     auto pos = static_cast<std::string::size_type>(alnum - sql.begin());
140     /* 3 represents the number of prefix characters that need to be extracted and checked */
141     if (pos + 3 >= sql.length()) {
142         return STATEMENT_ERROR;
143     }
144     /* analyze the sql type through first 3 characters */
145     std::string prefixSql = StrToUpper(sql.substr(pos, 3));
146     SqlType type = { prefixSql.c_str(), STATEMENT_OTHER };
147     auto comp = [](const SqlType &first, const SqlType &second) {
148         return strcmp(first.sql, second.sql) < 0;
149     };
150     auto it = std::lower_bound(SQL_TYPE_MAP, SQL_TYPE_MAP + TYPE_SIZE, type, comp);
151     if (it < SQL_TYPE_MAP + TYPE_SIZE && !comp(type, *it)) {
152         return it->type;
153     }
154     return STATEMENT_OTHER;
155 }
156 
StrToUpper(const std::string & s)157 std::string SqliteUtils::StrToUpper(const std::string &s)
158 {
159     std::string str = s;
160     std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::toupper(c); });
161     return str;
162 }
163 
Replace(const std::string & src,const std::string & rep,const std::string & dst)164 std::string SqliteUtils::Replace(const std::string &src, const std::string &rep, const std::string &dst)
165 {
166     if (src.empty() || rep.empty()) {
167         return "";
168     }
169     auto res = src;
170     size_t pos = 0;
171     while ((pos = res.find(rep, pos)) != std::string::npos) {
172         res.replace(pos, rep.length(), dst);
173         pos += dst.length();
174     }
175     return res;
176 }
177 
IsSupportSqlForExecute(int sqlType)178 bool SqliteUtils::IsSupportSqlForExecute(int sqlType)
179 {
180     return (sqlType == STATEMENT_DDL || sqlType == STATEMENT_INSERT || sqlType == STATEMENT_UPDATE ||
181             sqlType == STATEMENT_PRAGMA);
182 }
183 
IsSqlReadOnly(int sqlType)184 bool SqliteUtils::IsSqlReadOnly(int sqlType)
185 {
186     return (sqlType == STATEMENT_SELECT);
187 }
188 
IsSpecial(int sqlType)189 bool SqliteUtils::IsSpecial(int sqlType)
190 {
191     if (sqlType == STATEMENT_BEGIN || sqlType == STATEMENT_COMMIT || sqlType == STATEMENT_ROLLBACK) {
192         return true;
193     }
194     return false;
195 }
196 
GetConflictClause(int conflictResolution)197 const char *SqliteUtils::GetConflictClause(int conflictResolution)
198 {
199     if (conflictResolution < 0 || conflictResolution >= CONFLICT_CLAUSE_COUNT) {
200         return nullptr;
201     }
202     return ON_CONFLICT_CLAUSE[conflictResolution];
203 }
204 
DeleteFile(const std::string & filePath)205 bool SqliteUtils::DeleteFile(const std::string &filePath)
206 {
207     if (access(filePath.c_str(), F_OK) != 0) {
208         return true;
209     }
210     auto ret = remove(filePath.c_str());
211     if (ret != 0) {
212         LOG_WARN(
213             "remove file failed errno %{public}d ret %{public}d %{public}s", errno, ret, Anonymous(filePath).c_str());
214         return false;
215     }
216     return true;
217 }
218 
RenameFile(const std::string & srcFile,const std::string & destFile)219 bool SqliteUtils::RenameFile(const std::string &srcFile, const std::string &destFile)
220 {
221     auto ret = rename(srcFile.c_str(), destFile.c_str());
222     if (ret != 0) {
223         LOG_WARN("rename failed errno %{public}d ret %{public}d %{public}s -> %{public}s", errno, ret,
224             SqliteUtils::Anonymous(destFile).c_str(), SqliteUtils::Anonymous(srcFile).c_str());
225         return false;
226     }
227     return true;
228 }
229 
CopyFile(const std::string & srcFile,const std::string & destFile)230 bool SqliteUtils::CopyFile(const std::string &srcFile, const std::string &destFile)
231 {
232     std::ifstream src(srcFile.c_str(), std::ios::binary);
233     if (!src.is_open()) {
234         LOG_WARN("open srcFile failed errno %{public}d %{public}s", errno, SqliteUtils::Anonymous(srcFile).c_str());
235         return false;
236     }
237     std::ofstream dst(destFile.c_str(), std::ios::binary);
238     if (!dst.is_open()) {
239         src.close();
240         LOG_WARN("open destFile failed errno %{public}d %{public}s", errno, SqliteUtils::Anonymous(destFile).c_str());
241         return false;
242     }
243     dst << src.rdbuf();
244     src.close();
245     dst.close();
246     return true;
247 }
248 
DeleteFolder(const std::string & folderPath)249 size_t SqliteUtils::DeleteFolder(const std::string &folderPath)
250 {
251 #if !defined(CROSS_PLATFORM)
252     std::error_code ec;
253     size_t count = std::filesystem::remove_all(folderPath, ec);
254     auto errorCount = static_cast<std::uintmax_t>(-1);
255     if (count == errorCount) {
256         LOG_WARN("remove folder, %{public}d, %{public}s, %{public}s", ec.value(),
257             ec.message().c_str(), Anonymous(folderPath).c_str());
258         count = 0;
259     }
260     return count;
261 #else
262     return 0;
263 #endif
264 }
265 
IsKeyword(const std::string & word)266 bool SqliteUtils::IsKeyword(const std::string &word)
267 {
268     return IsMatchKeyword(SQL_KEYWORD, sizeof(SQL_KEYWORD) / sizeof(char *), StrToUpper(word).c_str()) ||
269            IsMatchKeyword(WHILE_KEYWORDS, sizeof(WHILE_KEYWORDS) / sizeof(char *), StrToUpper(word).c_str());
270 }
271 
GetAnonymousName(const std::string & fileName)272 std::string SqliteUtils::GetAnonymousName(const std::string &fileName)
273 {
274     if (fileName.empty()) {
275         return "";
276     }
277 
278     if (fileName.length() <= HEAD_SIZE) {
279         return fileName.substr(0, 1) + "**";
280     }
281 
282     if (fileName.length() < MIN_SIZE) {
283         return (fileName.substr(0, HEAD_SIZE) + REPLACE_CHAIN);
284     }
285 
286     return (fileName.substr(0, HEAD_SIZE) + REPLACE_CHAIN + fileName.substr(fileName.length() - END_SIZE, END_SIZE));
287 }
288 
AnonymousDigits(const std::string & digits)289 std::string SqliteUtils::AnonymousDigits(const std::string &digits)
290 {
291     std::string::size_type digitsNum = digits.size();
292     if (digitsNum < CONTINUOUS_DIGITS_MINI_SIZE) {
293         return digits;
294     }
295     std::string::size_type endDigitsNum = 4;
296     std::string::size_type shortEndDigitsNum = 3;
297     std::string name = digits;
298     std::string last = "";
299     if (digitsNum == CONTINUOUS_DIGITS_MINI_SIZE) {
300         last = name.substr(name.size() - shortEndDigitsNum);
301     } else {
302         last = name.substr(name.size() - endDigitsNum);
303     }
304     return "***" + last;
305 }
306 
ByteAnonymous(const std::string & input)307 std::string ByteAnonymous(const std::string &input)
308 {
309     std::string output;
310     bool maskCurrent = false;
311     for (unsigned char byte : input) {
312         if (byte > MAX_PRINTABLE_BYTE) {
313             if (!maskCurrent) {
314                 output += "***";
315                 maskCurrent = true;
316             }
317         } else {
318             output.push_back(static_cast<char>(byte));
319             maskCurrent = false;
320         }
321     }
322     return output;
323 }
324 
SqlAnonymous(const std::string & sql)325 std::string SqliteUtils::SqlAnonymous(const std::string &sql)
326 {
327     std::ostringstream result;
328     std::regex idRegex(R"(\b[a-zA-Z0-9_]+\b)");
329     auto begin = std::sregex_iterator(sql.begin(), sql.end(), idRegex);
330     auto end = std::sregex_iterator();
331 
332     size_t lastPos = 0;
333     for (auto it = begin; it != end; ++it) {
334         std::smatch match = *it;
335         std::string word = match.str();
336         size_t pos = static_cast<size_t>(match.position());
337 
338         result << ByteAnonymous(sql.substr(lastPos, pos - lastPos));
339 
340         lastPos = pos + word.length();
341         if (std::regex_match(word, std::regex(R"(\b[0-9a-fA-F]+\b)"))) {
342             result << AnonymousDigits(word);
343         } else if (IsKeyword(word)) {
344             result << std::move(word);
345         } else {
346             result << GetAnonymousName(word);
347         }
348     }
349 
350     result << ByteAnonymous(sql.substr(lastPos));
351     return result.str();
352 }
353 
Anonymous(const std::string & srcFile)354 std::string SqliteUtils::Anonymous(const std::string &srcFile)
355 {
356     auto pre = srcFile.find("/");
357     auto end = srcFile.rfind("/");
358     if (pre == std::string::npos || end - pre < FILE_PATH_MINI_SIZE) {
359         return GetAnonymousName(srcFile);
360     }
361     auto path = srcFile.substr(pre, end - pre);
362     auto area = path.find("/el");
363     if (area == std::string::npos || area + AREA_MINI_SIZE > path.size()) {
364         path = "";
365     } else if (area + AREA_OFFSET_SIZE < path.size()) {
366         path = path.substr(area, AREA_MINI_SIZE) + "/***";
367     } else {
368         path = path.substr(area, AREA_MINI_SIZE);
369     }
370     std::string fileName = srcFile.substr(end + 1); // rdb file name
371     fileName = GetAnonymousName(fileName);
372     return srcFile.substr(0, pre + PRE_OFFSET_SIZE) + "***" + path + "/" + fileName;
373 }
374 
GetArea(const std::string & srcFile)375 std::string SqliteUtils::GetArea(const std::string &srcFile)
376 {
377     size_t start = srcFile.find("/el");
378     if (start == std::string::npos) {
379         return "";
380     }
381     size_t end = srcFile.find("/", start + 1);
382     if (end != std::string::npos) {
383         return srcFile.substr(start + 1, end - start - 1);
384     }
385     return "";
386 }
387 
GetFileSize(const std::string & fileName)388 ssize_t SqliteUtils::GetFileSize(const std::string &fileName)
389 {
390     struct stat fileStat;
391     if (fileName.empty() || stat(fileName.c_str(), &fileStat) < 0) {
392         if (errno != ENOENT) {
393             LOG_ERROR("failed, errno: %{public}d, fileName:%{public}s", errno, Anonymous(fileName).c_str());
394         }
395         return 0;
396     }
397 
398     return fileStat.st_size;
399 }
400 
IsSlaveDbName(const std::string & fileName)401 bool SqliteUtils::IsSlaveDbName(const std::string &fileName)
402 {
403     std::string slaveSuffix("_slave.db");
404     if (fileName.size() < slaveSuffix.size()) {
405         return false;
406     }
407     size_t pos = fileName.rfind(slaveSuffix);
408     return (pos != std::string::npos) && (pos == fileName.size() - slaveSuffix.size());
409 }
410 
GetSlavePath(const std::string & name)411 std::string SqliteUtils::GetSlavePath(const std::string &name)
412 {
413     std::string suffix(".db");
414     std::string slaveSuffix("_slave.db");
415     auto pos = name.rfind(suffix);
416     if (pos == std::string::npos || pos < name.length() - suffix.length()) {
417         return name + slaveSuffix;
418     }
419     return name.substr(0, pos) + slaveSuffix;
420 }
421 
HmacAlgoDescription(int32_t hmacAlgo)422 const char *SqliteUtils::HmacAlgoDescription(int32_t hmacAlgo)
423 {
424     HmacAlgo hmacEnum = static_cast<HmacAlgo>(hmacAlgo);
425     switch (hmacEnum) {
426         case HmacAlgo::SHA1:
427             return "sha1";
428         case HmacAlgo::SHA256:
429             return "sha256";
430         case HmacAlgo::SHA512:
431             return "sha512";
432         default:
433             return "sha256";
434     }
435 }
436 
KdfAlgoDescription(int32_t kdfAlgo)437 const char *SqliteUtils::KdfAlgoDescription(int32_t kdfAlgo)
438 {
439     KdfAlgo kdfEnum = static_cast<KdfAlgo>(kdfAlgo);
440     switch (kdfEnum) {
441         case KdfAlgo::KDF_SHA1:
442             return "kdf_sha1";
443         case KdfAlgo::KDF_SHA256:
444             return "kdf_sha256";
445         case KdfAlgo::KDF_SHA512:
446             return "kdf_sha512";
447         default:
448             return "kdf_sha256";
449     }
450 }
451 
EncryptAlgoDescription(int32_t encryptAlgo)452 const char *SqliteUtils::EncryptAlgoDescription(int32_t encryptAlgo)
453 {
454     EncryptAlgo encryptEnum = static_cast<EncryptAlgo>(encryptAlgo);
455     switch (encryptEnum) {
456         case EncryptAlgo::AES_256_CBC:
457             return "aes-256-cbc";
458         case EncryptAlgo::AES_256_GCM:
459         default:
460             return "aes-256-gcm";
461     }
462 }
463 
SetSlaveInvalid(const std::string & dbPath)464 int SqliteUtils::SetSlaveInvalid(const std::string &dbPath)
465 {
466     if (IsSlaveInvalid(dbPath)) {
467         return E_OK;
468     }
469     std::ofstream src((dbPath + SLAVE_FAILURE).c_str(), std::ios::binary);
470     if (src.is_open()) {
471         src.close();
472         return E_OK;
473     }
474     return E_ERROR;
475 }
476 
SetSlaveInterrupted(const std::string & dbPath)477 int SqliteUtils::SetSlaveInterrupted(const std::string &dbPath)
478 {
479     if (IsSlaveInterrupted(dbPath)) {
480         return E_OK;
481     }
482     std::ofstream src((dbPath + SLAVE_INTERRUPT).c_str(), std::ios::binary);
483     if (src.is_open()) {
484         src.close();
485         return E_OK;
486     }
487     return E_ERROR;
488 }
489 
IsSlaveInvalid(const std::string & dbPath)490 bool SqliteUtils::IsSlaveInvalid(const std::string &dbPath)
491 {
492     return access((dbPath + SLAVE_FAILURE).c_str(), F_OK) == 0;
493 }
494 
IsSlaveInterrupted(const std::string & dbPath)495 bool SqliteUtils::IsSlaveInterrupted(const std::string &dbPath)
496 {
497     return access((dbPath + SLAVE_INTERRUPT).c_str(), F_OK) == 0;
498 }
499 
GetPageCountCallback(void * data,int argc,char ** argv,char ** azColName)500 int SqliteUtils::GetPageCountCallback(void *data, int argc, char **argv, char **azColName)
501 {
502     int64_t *count = (int64_t *)data;
503     if (argc > 0 && argv[0] != NULL) {
504         char *endptr = nullptr;
505         *count = static_cast<int64_t>(strtoll(argv[0], &endptr, 10)); // 10 means decimal
506     }
507     return 0;
508 }
509 
GetDecompressedSize(const std::string & dbPath)510 ssize_t SqliteUtils::GetDecompressedSize(const std::string &dbPath)
511 {
512     sqlite3 *dbHandle = nullptr;
513     int errCode = sqlite3_open_v2(dbPath.c_str(), &dbHandle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, nullptr);
514     if (errCode != SQLITE_OK) {
515         LOG_WARN("failed to open %{public}s to calculate size", Anonymous(dbPath).c_str());
516         sqlite3_close_v2(dbHandle);
517         return 0;
518     }
519     int64_t pageCount = 0;
520     errCode = sqlite3_exec(dbHandle, "SELECT COUNT(1) FROM vfs_pages;", GetPageCountCallback, &pageCount, nullptr);
521     sqlite3_close_v2(dbHandle);
522     if (errCode != SQLITE_OK) {
523         LOG_WARN("failed to get page count, %{public}s", Anonymous(dbPath).c_str());
524         return 0;
525     }
526     // 4096 is the pageSize, which is 4K
527     auto size = pageCount * 4096;
528     if (size > SSIZE_MAX) {
529         LOG_WARN("actual size overflow:%{public}" PRId64, size);
530         return SSIZE_MAX;
531     }
532     return (ssize_t)size;
533 }
534 
IsSlaveLarge(const std::string & dbPath)535 bool SqliteUtils::IsSlaveLarge(const std::string &dbPath)
536 {
537     auto slavePath = GetSlavePath(dbPath);
538     if (sqlite3_is_support_binlog(StringUtils::ExtractFileName(dbPath).c_str()) == SQLITE_OK) {
539         auto size = GetDecompressedSize(slavePath);
540         if (size > 0) {
541             return size > SLAVE_ASYNC_REPAIR_CHECK_LIMIT;
542         }
543     }
544     std::pair<int32_t, DistributedRdb::RdbDebugInfo> fileInfo = Stat(slavePath);
545     if (fileInfo.first == E_OK) {
546         return fileInfo.second.size_ > SLAVE_ASYNC_REPAIR_CHECK_LIMIT;
547     }
548     return false;
549 }
550 
SetSlaveRestoring(const std::string & dbPath,bool isRestore)551 int SqliteUtils::SetSlaveRestoring(const std::string &dbPath, bool isRestore)
552 {
553     if (IsSlaveRestoring(dbPath)) {
554         if (!isRestore) {
555             std::remove((dbPath + SLAVE_RESTORE).c_str());
556         }
557         return E_OK;
558     }
559     std::ofstream src((dbPath + SLAVE_RESTORE).c_str(), std::ios::binary);
560     if (src.is_open()) {
561         src.close();
562         return E_OK;
563     }
564     return E_ERROR;
565 }
566 
IsSlaveRestoring(const std::string & dbPath)567 bool SqliteUtils::IsSlaveRestoring(const std::string &dbPath)
568 {
569     return access((dbPath + SLAVE_RESTORE).c_str(), F_OK) == 0;
570 }
571 
SetSlaveValid(const std::string & dbPath)572 void SqliteUtils::SetSlaveValid(const std::string &dbPath)
573 {
574     std::remove((dbPath + SLAVE_INTERRUPT).c_str());
575     std::remove((dbPath + SLAVE_FAILURE).c_str());
576 }
577 
DeleteDirtyFiles(const std::string & backupFilePath)578 bool SqliteUtils::DeleteDirtyFiles(const std::string &backupFilePath)
579 {
580     auto res = DeleteFile(backupFilePath);
581     res = DeleteFile(backupFilePath + "-shm") && res;
582     res = DeleteFile(backupFilePath + "-wal") && res;
583     return res;
584 }
585 
Stat(const std::string & path)586 std::pair<int32_t, DistributedRdb::RdbDebugInfo> SqliteUtils::Stat(const std::string &path)
587 {
588     DistributedRdb::RdbDebugInfo info;
589     struct stat fileStat;
590     if (stat(path.c_str(), &fileStat) != 0) {
591         return std::pair{ E_ERROR, info };
592     }
593     info.inode_ = fileStat.st_ino;
594     info.oldInode_ = 0;
595     info.atime_.sec_ = fileStat.st_atime;
596     info.mtime_.sec_ = fileStat.st_mtime;
597     info.ctime_.sec_ = fileStat.st_ctime;
598 #if !defined(CROSS_PLATFORM)
599     info.atime_.nsec_ = fileStat.st_atim.tv_nsec;
600     info.mtime_.nsec_ = fileStat.st_mtim.tv_nsec;
601     info.ctime_.nsec_ = fileStat.st_ctim.tv_nsec;
602 #endif
603     info.size_ = fileStat.st_size;
604     info.dev_ = fileStat.st_dev;
605     info.mode_ = fileStat.st_mode;
606     info.uid_ = fileStat.st_uid;
607     info.gid_ = fileStat.st_gid;
608     return std::pair{ E_OK, info };
609 }
610 
ReadFileHeader(const std::string & filePath)611 std::string SqliteUtils::ReadFileHeader(const std::string &filePath)
612 {
613     constexpr int MAX_SIZE = 98;
614     std::ifstream file(filePath, std::ios::binary);
615     uint8_t data[MAX_SIZE] = {0};
616     if (file.is_open()) {
617         file.read(reinterpret_cast<char *>(data), MAX_SIZE);
618         file.close();
619     }
620     std::stringstream ss;
621     for (int i = 0; i < MAX_SIZE; i++) {
622         ss << std::hex << std::setw(DISPLAY_BYTE) << std::setfill('0') << static_cast<int>(data[i]);
623     }
624     return "DB_HEAD:" + ss.str();
625 }
626 
GetFileStatInfo(const DebugInfo & debugInfo)627 std::string SqliteUtils::GetFileStatInfo(const DebugInfo &debugInfo)
628 {
629     std::stringstream oss;
630     oss << " dev:0x" << std::hex << debugInfo.dev_ << " ino:0x" << std::hex << debugInfo.inode_;
631     if (debugInfo.inode_ != debugInfo.oldInode_ && debugInfo.oldInode_ != 0) {
632         oss << "<>0x" << std::hex << debugInfo.oldInode_;
633     }
634     oss << " " << GetModeInfo(debugInfo.mode_) << " size:" << std::dec << debugInfo.size_ << " uid:" << std::dec
635         << debugInfo.uid_ << " gid:" << std::dec << debugInfo.gid_
636         << " atim:" << RdbTimeUtils::GetTimeWithMs(debugInfo.atime_.sec_, debugInfo.atime_.nsec_)
637         << " mtim:" << RdbTimeUtils::GetTimeWithMs(debugInfo.mtime_.sec_, debugInfo.mtime_.nsec_)
638         << " ctim:" << RdbTimeUtils::GetTimeWithMs(debugInfo.ctime_.sec_, debugInfo.ctime_.nsec_);
639     return oss.str();
640 }
641 
CleanFileContent(const std::string & filePath)642 bool SqliteUtils::CleanFileContent(const std::string &filePath)
643 {
644     struct stat fileStat;
645     if (stat(filePath.c_str(), &fileStat) != 0) {
646         return false;
647     }
648     if (fileStat.st_size < FILE_MAX_SIZE) {
649         return false;
650     }
651     return DeleteFile(filePath);
652 }
653 
WriteSqlToFile(const std::string & comparePath,const std::string & sql)654 void SqliteUtils::WriteSqlToFile(const std::string &comparePath, const std::string &sql)
655 {
656     int fd = open(comparePath.c_str(), O_RDWR | O_CREAT, 0660);
657     if (fd == -1) {
658         LOG_ERROR("open file failed errno %{public}d %{public}s", errno, Anonymous(comparePath).c_str());
659         return ;
660     }
661     if (flock(fd, LOCK_EX) == -1) {
662         LOG_ERROR("Failed to lock file errno %{public}d %{public}s", errno, Anonymous(comparePath).c_str());
663         close(fd);
664         return ;
665     }
666     std::ofstream outFile(comparePath, std::ios::app);
667     if (!outFile) {
668         flock(fd, LOCK_UN);
669         close(fd);
670         return ;
671     }
672 
673     outFile << sql << "\n";
674     outFile.close();
675     if (flock(fd, LOCK_UN) == -1) {
676         LOG_ERROR("Failed to unlock file errno %{public}d %{public}s", errno, Anonymous(comparePath).c_str());
677     }
678     close(fd);
679 }
680 
GetErrInfoFromMsg(const std::string & message,const std::string & errStr)681 std::string SqliteUtils::GetErrInfoFromMsg(const std::string &message, const std::string &errStr)
682 {
683     size_t startPos = message.find(errStr);
684     std::string result;
685     if (startPos != std::string::npos) {
686         startPos += errStr.length();
687         size_t endPos = message.length();
688         result = message.substr(startPos, endPos - startPos);
689     }
690     return result;
691 }
692 
CompareTableFileContent(const std::string & dbPath,const std::string & bundleName,const std::string & tableName)693 ErrMsgState SqliteUtils::CompareTableFileContent(
694     const std::string &dbPath, const std::string &bundleName, const std::string &tableName)
695 {
696     ErrMsgState state;
697     std::string compareFilePath = dbPath + "-compare";
698     std::ifstream file(compareFilePath.c_str());
699     if (!file.is_open()) {
700         LOG_ERROR("compare File open failed errno %{public}d %{public}s", errno, Anonymous(compareFilePath).c_str());
701         return state;
702     }
703 
704     std::string line;
705     while (getline(file, line)) {
706         std::string target = line;
707         if (target.find(tableName) == std::string::npos) {
708             continue;
709         }
710         std::transform(target.begin(), target.end(), target.begin(), ::toupper);
711         if (target.substr(0, PREFIX_LENGTH) == "CRE") {
712             state.isCreated = true;
713             state.isDeleted = false;
714             state.isRenamed = false;
715         } else if (target.substr(0, PREFIX_LENGTH) == "DRO") {
716             state.isDeleted = true;
717             state.isRenamed = false;
718         } else if (target.substr(0, PREFIX_LENGTH) == "ALT" && target.find("RENAME") != std::string::npos) {
719             state.isDeleted = false;
720             state.isRenamed = true;
721         }
722     }
723     file.close();
724     return state;
725 }
726 
CompareColumnFileContent(const std::string & dbPath,const std::string & bundleName,const std::string & columnName)727 ErrMsgState SqliteUtils::CompareColumnFileContent(
728     const std::string &dbPath, const std::string &bundleName, const std::string &columnName)
729 {
730     ErrMsgState state;
731     std::string compareFilePath = dbPath + "-compare";
732     std::ifstream file(compareFilePath.c_str());
733     if (!file.is_open()) {
734         LOG_ERROR("compare File open failed errno %{public}d %{public}s", errno, Anonymous(compareFilePath).c_str());
735         return state;
736     }
737 
738     std::string line;
739     while (getline(file, line)) {
740         std::string target = line;
741         if (target.find(columnName) == std::string::npos) {
742             continue;
743         }
744         std::transform(target.begin(), target.end(), target.begin(), ::toupper);
745         if (target.substr(0, PREFIX_LENGTH) == "CRE" || (
746             target.substr(0, PREFIX_LENGTH) == "ALT" && target.find("ADD") != std::string::npos)) {
747             state.isCreated = true;
748             state.isDeleted = false;
749             state.isRenamed = false;
750         } else if (target.substr(0, PREFIX_LENGTH) == "ALT" && target.find("DROP") != std::string::npos) {
751             state.isDeleted = true;
752             state.isRenamed = false;
753         } else if (target.substr(0, PREFIX_LENGTH) == "ALT" && target.find("RENAME") != std::string::npos) {
754             state.isDeleted = false;
755             state.isRenamed = true;
756         }
757     }
758     file.close();
759     return state;
760 }
761 
FormatDebugInfo(const std::map<std::string,DebugInfo> & debugs,const std::string & header)762 std::string SqliteUtils::FormatDebugInfo(const std::map<std::string, DebugInfo> &debugs, const std::string &header)
763 {
764     if (debugs.empty()) {
765         return "";
766     }
767     std::string appendix = header;
768     for (auto &[name, debugInfo] : debugs) {
769         appendix += "\n" + name + " :" + GetFileStatInfo(debugInfo);
770     }
771     return appendix;
772 }
773 
FormatDebugInfoBrief(const std::map<std::string,DebugInfo> & debugs,const std::string & header)774 std::string SqliteUtils::FormatDebugInfoBrief(const std::map<std::string, DebugInfo> &debugs,
775     const std::string &header)
776 {
777     if (debugs.empty()) {
778         return "";
779     }
780     std::stringstream oss;
781     oss << header << ":";
782     for (auto &[name, debugInfo] : debugs) {
783         oss << "<" << name << ",0x" << std::hex << debugInfo.inode_ << "," << std::dec << debugInfo.size_ << ","
784             << std::oct << debugInfo.mode_ << ">";
785     }
786     return oss.str();
787 }
FormatDfxInfo(const DfxInfo & dfxInfo)788 std::string SqliteUtils::FormatDfxInfo(const DfxInfo &dfxInfo)
789 {
790     std::stringstream oss;
791     oss << "LastOpen:" << dfxInfo.lastOpenTime_ << "," << "CUR_USER:" << dfxInfo.curUserId_;
792     return oss.str();
793 }
794 
GetModeInfo(uint32_t st_mode)795 std::string SqliteUtils::GetModeInfo(uint32_t st_mode)
796 {
797     std::ostringstream oss;
798     const uint32_t permission = 0777;
799     oss << "mode:";
800     if (S_ISDIR(st_mode))
801         oss << 'd';
802     else
803         oss << '-';
804 
805     oss << std::setw(PREFIX_LENGTH) << std::setfill('0') << std::oct << (st_mode & permission);
806 
807     return oss.str();
808 }
809 
GetParentModes(const std::string & path,int pathDepth)810 std::string SqliteUtils::GetParentModes(const std::string &path, int pathDepth)
811 {
812     std::vector<std::pair<std::string, std::string>> dirModes;
813     std::string currentPath = path;
814 
815     for (int i = 0; i < pathDepth; ++i) {
816         currentPath = StringUtils::GetParentPath(currentPath);
817         if (currentPath == "/" || currentPath.empty()) {
818             break;
819         }
820 
821         std::string dirName = StringUtils::ExtractFileName(currentPath);
822         struct stat st {};
823         dirModes.emplace_back(dirName, (stat(currentPath.c_str(), &st) == 0) ? GetModeInfo(st.st_mode) : "access_fail");
824     }
825     std::string result;
826     for (auto it = dirModes.rbegin(); it != dirModes.rend(); ++it) {
827         if (!result.empty()) {
828             result += " <- ";
829         }
830         result += (it->first.size() > PREFIX_LENGTH ? it->first.substr(0, PREFIX_LENGTH) + "***" : it->first) + ":" +
831                   it->second;
832     }
833     return result.empty() ? "no_parent" : result;
834 }
835 } // namespace NativeRdb
836 } // namespace OHOS
837