#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
+#include <sys/stat.h>
+#include <stdio.h>
#include <glib.h>
#include <sqlite3.h>
#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 " \
"PRIMARY KEY(key)) "
static GHashTable *dbs;
+static bool is_db_corrupted;
static void free_db(sqlite3 *db)
{
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;
}
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);
.unset_value = unset_value,
.list_keys = list_keys,
.get_dump = get_dump,
+ .check_db = check_db,
};