Add db intergrity check 26/258926/8
authorjusung <jusung07.son@samsung.com>
Fri, 28 May 2021 02:10:40 +0000 (11:10 +0900)
committerjusung <jusung07.son@samsung.com>
Fri, 28 May 2021 04:49:57 +0000 (13:49 +0900)
Change-Id: I5455149b0424a490ad21e31cb6ddfbe3928ae4a4
Signed-off-by: jusung <jusung07.son@samsung.com>
backend/sqlite.c
common/backend.h
common/direct.c
common/direct.h
daemon/daemon.c

index 5ec0b7d..c7bf8b6 100644 (file)
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <fcntl.h>
 #include <stdbool.h>
+#include <sys/stat.h>
+#include <stdio.h>
 
 #include <glib.h>
 #include <sqlite3.h>
@@ -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,
 };
 
index 7db0627..db51677 100644 (file)
@@ -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];
 };
index cabc3a9..bebb250 100644 (file)
@@ -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;
index b8f3385..1dc7af2 100644 (file)
@@ -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);
+
index 903bfd6..bd01d67 100644 (file)
@@ -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;