From 10942c3eafe37e3848621bdae2baf996c2ab25d3 Mon Sep 17 00:00:00 2001 From: jusung Date: Fri, 28 May 2021 11:10:40 +0900 Subject: [PATCH] Add db intergrity check Change-Id: I5455149b0424a490ad21e31cb6ddfbe3928ae4a4 Signed-off-by: jusung --- backend/sqlite.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- common/backend.h | 2 + common/direct.c | 30 +++++++++ common/direct.h | 3 + daemon/daemon.c | 11 ++++ 5 files changed, 227 insertions(+), 2 deletions(-) diff --git a/backend/sqlite.c b/backend/sqlite.c index 5ec0b7d..c7bf8b6 100644 --- a/backend/sqlite.c +++ b/backend/sqlite.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -34,6 +36,7 @@ #define BUXTON_DEFAULT_WAL_AUTOCHECKPOINT 100 #define BASE_DB_PATH tzplatform_getenv(TZ_SYS_RO_ETC) +#define MAX_PATH_LEN 512 #define QUERY_MAX_LEN 8192 #define QUERY_CREATE_TABLE_BUXTON "create table if not exists buxton " \ @@ -42,6 +45,7 @@ "PRIMARY KEY(key)) " static GHashTable *dbs; +static bool is_db_corrupted; static void free_db(sqlite3 *db) { @@ -310,8 +314,9 @@ static int get_value(const char *dbpath, const char *key, void **data, r = sqlite3_prepare_v2(db, select_query, strlen(select_query), &stmt, NULL); if (r != SQLITE_OK) { - bxt_err("prepare error, ret = %d, extended = %d\n", - r, sqlite3_extended_errcode(db)); + bxt_err("prepare error, ret = %d, extended = %d %s", + r, sqlite3_extended_errcode(db), + sqlite3_errmsg(db)); return BUXTON_ERROR_IO_ERROR; } @@ -612,6 +617,179 @@ end: return ret; } +static int check_integrity_cb(void *pid, int argc, char **argv, char **notUsed) +{ + if (strcmp(argv[0], "ok")) { + bxt_err("db integrity result : %s", argv[0]); + is_db_corrupted = true; + return -1; + } + + bxt_dbg("db integrity result : %s", argv[0]); + return 0; +} + +static void delete_db_file(const char *dbpath) +{ + char target_path[MAX_PATH_LEN]; + int ret; + + bxt_info("delet db file %s", dbpath); + + ret = unlink(dbpath); + if (ret != 0) + bxt_err("unlink(%s) is failed. errno(%d)", dbpath, ret); + + snprintf(target_path, sizeof(target_path), "%s-shm", dbpath); + ret = unlink(target_path); + if (ret != 0) + bxt_err("unlink(%s) is failed. errno(%d)", target_path, ret); + + snprintf(target_path, sizeof(target_path), "%s-wal", dbpath); + ret = unlink(target_path); + if (ret != 0) + bxt_err("unlink(%s) is failed. errno(%d)", target_path, ret); +} + +/* LCOV_EXCL_START */ +static int recover_corrupted_db(const char *dbpath) +{ + int sql_ret; + sqlite3 *db; + + bxt_info("DB is corrupted, start to recover corrupted db %s", dbpath); + + delete_db_file(dbpath); + + sql_ret = sqlite3_open_v2(dbpath, &db, + SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, + NULL); + if (sql_ret != SQLITE_OK) { + bxt_err("Failed to open db[%s : %d] %s", + dbpath, sql_ret, sqlite3_errmsg(db)); + return BUXTON_ERROR_IO_ERROR; + } + + sql_ret = sqlite3_exec(db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL); + if (sql_ret) { + bxt_err("change journal mode '%s' failed: %s", + dbpath, sqlite3_errmsg(db)); + sqlite3_close(db); + return BUXTON_ERROR_IO_ERROR; + } + + sql_ret = sqlite3_exec(db, QUERY_CREATE_TABLE_BUXTON, NULL, NULL, NULL); + if (sql_ret != SQLITE_OK) { + bxt_err("Create tables '%s' failed: %s", + dbpath, sqlite3_errmsg(db)); + + sqlite3_close(db); + return BUXTON_ERROR_IO_ERROR; + } + + sql_ret = sqlite3_wal_autocheckpoint( + db, BUXTON_DEFAULT_WAL_AUTOCHECKPOINT); + if (sql_ret != SQLITE_OK) + bxt_err("SET DEFAULT_WAL_AUTOCHECKPOINT failed : %d", sql_ret); + + sqlite3_close(db); + + return BUXTON_ERROR_NONE; +} +/* LCOV_EXCL_STOP */ + +static int check_integrity(sqlite3 *db) +{ + int ret; + + ret = sqlite3_exec(db, "PRAGMA integrity_check", + check_integrity_cb, NULL, NULL); + if (ret != SQLITE_OK || is_db_corrupted) { + bxt_err("Failed to exec query[%d][%s]", ret, sqlite3_errmsg(db)); + return BUXTON_ERROR_IO_ERROR; + } + return BUXTON_ERROR_NONE; +} + +static int check_table(sqlite3 *db) +{ + int ret; + int count = 0; + const char query[] = + "SELECT count(name) FROM sqlite_master WHERE type='table'"; + sqlite3_stmt *stmt; + + ret = sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL); + if (ret != SQLITE_OK) { + bxt_err("prepare error: %s", sqlite3_errmsg(db)); + return BUXTON_ERROR_IO_ERROR; + } + + ret = sqlite3_step(stmt); + if(ret == SQLITE_ROW) + count = sqlite3_column_int(stmt, 0); + + if (count == 0) { + bxt_err("table error: %d", ret); + ret = BUXTON_ERROR_IO_ERROR; + } else { + ret = BUXTON_ERROR_NONE; + } + + sqlite3_finalize(stmt); + + return ret; +} + +static int check_owner(const char *dbpath) +{ + struct stat info; + + stat(dbpath, &info); + if (info.st_uid != getuid()) { + bxt_err("invalid owner [%d : %d]", getuid(), info.st_uid); + return BUXTON_ERROR_IO_ERROR; + } + + return BUXTON_ERROR_NONE; +} + +static int check_db(const char *dbpath) +{ + int ret; + sqlite3 *db; + + ret = sqlite3_open_v2(dbpath, &db, + SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); + if (ret != SQLITE_OK) { + bxt_err("Failed to open db[%s : %d] %s", + dbpath, ret, sqlite3_errmsg(db)); + ret = BUXTON_ERROR_IO_ERROR; + goto out; + } + + ret = check_owner(dbpath); + if (ret != BUXTON_ERROR_NONE) + goto out; + + ret = check_integrity(db); + if (ret != BUXTON_ERROR_NONE) + goto out; + + ret = check_table(db); + if (ret != BUXTON_ERROR_NONE) + goto out; + +out: + if (db) + sqlite3_close(db); + + if (ret != BUXTON_ERROR_NONE) + ret = recover_corrupted_db(dbpath); + + return ret; +} + static void module_exit(void) { g_hash_table_destroy(dbs); @@ -644,5 +822,6 @@ DEFINE_BUXTON_BACKEND = { .unset_value = unset_value, .list_keys = list_keys, .get_dump = get_dump, + .check_db = check_db, }; diff --git a/common/backend.h b/common/backend.h index 7db0627..db51677 100644 --- a/common/backend.h +++ b/common/backend.h @@ -40,6 +40,7 @@ typedef int (*backend_list_keys)(const char *dbpath, char ***keys, unsigned int *klen, bool readonly); typedef int (*backend_get_dump)(const char *dbpath, char ***keys, void ***values, int **value_len, unsigned int *klen); +typedef int (*backend_check_db)(const char *dbpath); struct backend { const char *name; @@ -55,6 +56,7 @@ struct backend { backend_unset_value unset_value; backend_list_keys list_keys; backend_get_dump get_dump; + backend_check_db check_db; void *reserved[7]; }; diff --git a/common/direct.c b/common/direct.c index cabc3a9..bebb250 100644 --- a/common/direct.c +++ b/common/direct.c @@ -757,6 +757,36 @@ void direct_exit(void) backend_exit(); } +int direct_db_check(const char *layer_name) +{ + int r; + const struct layer *ly; + const struct backend *backend; + char path[FILENAME_MAX]; + + r = conf_get_layer(layer_name, &ly); + if (r != BUXTON_ERROR_NONE) + return r; + + r = backend_get(ly->backend, &backend); + if (r != BUXTON_ERROR_NONE) + return r; + + if (!backend->check_db) { + bxt_err("backend '%s' has no db_check func", + backend->name); + return BUXTON_ERROR_INVALID_OPERATION; + } + + r = get_path(0, LAYER_ATTRIBUTE_RW, ly, path, sizeof(path)); + if (r != BUXTON_ERROR_NONE) + return r; + + r = backend->check_db(path); + + return r; +} + int direct_init(const char *moddir, const char *confpath, bool *log_on, int *max_line) { int r; diff --git a/common/direct.h b/common/direct.h index b8f3385..1dc7af2 100644 --- a/common/direct.h +++ b/common/direct.h @@ -44,3 +44,6 @@ int direct_remove_db(const struct buxton_layer *layer); int direct_dump(const struct buxton_layer *layer, char ***key_list, struct buxton_value **value_list, int *len); + +int direct_db_check(const char *layer_name); + diff --git a/daemon/daemon.c b/daemon/daemon.c index 903bfd6..bd01d67 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -46,6 +46,9 @@ #define BXT_LOG_FOLDER "/run/buxton2/log" #define BXT_NULL_STRING "(VCONF_NULL)" +#define BXT_SYSTEM_DB_LAYER "system" +#define BXT_SYSTEM_MEMORY_LAYER "memory" + static int _log_max_line = 2000; static bool _log_on = true; @@ -1129,6 +1132,14 @@ static int bxt_init(struct bxt_daemon *bxtd, const char *confpath) if (r != BUXTON_ERROR_NONE) return r; + r = direct_db_check(BXT_SYSTEM_DB_LAYER); + if (r != BUXTON_ERROR_NONE) + return r; + + r = direct_db_check(BXT_SYSTEM_MEMORY_LAYER); + if (r != BUXTON_ERROR_NONE) + return r; + bxtd->sk = sock_get_server(SOCKPATH); if (bxtd->sk < 0) return bxtd->sk; -- 2.7.4