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