Add db integrity check tool 61/163561/9
authorhyunho <hhstark.kang@samsung.com>
Tue, 12 Dec 2017 05:27:21 +0000 (14:27 +0900)
committerhyunho <hhstark.kang@samsung.com>
Wed, 13 Dec 2017 06:17:06 +0000 (15:17 +0900)
- widget_recovery

Change-Id: Id343043d17d458fed1c97e4e616d82494b9057b3
Signed-off-by: hyunho <hhstark.kang@samsung.com>
CMakeLists.txt
include/widget_service_internal.h
packaging/libwidget_service.spec
parser/widget.sql [deleted file]
src/widget_service.c
tool/CMakeLists.txt
tool/widget_recovery.c [new file with mode: 0644]

index cefea10befc9f71227b8d666a43c6ec9130e5dcc..b8d2c8689ec26fce89200993584345482e2aa2ac 100644 (file)
@@ -32,6 +32,7 @@ pkg_check_modules(pkgs REQUIRED
        cynara-client
        ecore-wayland
        iniparser
+       libsmack
 )
 
 FOREACH(flag ${pkgs_CFLAGS})
index 60f4234fca6fb868eee39e4663e07822c71a1902..891995cccf3fd12a5e4812cbf7cdbe97edd586ec 100644 (file)
@@ -22,6 +22,7 @@ extern "C" {
 #endif
 
 #include <bundle.h>
+#include <stdbool.h>
 
 /**
  * @internal
@@ -765,7 +766,7 @@ extern int widget_service_get_widget_max_count(const char *widget_id);
 extern int widget_service_get_auto_align(const char *widget_id);
 extern int widget_service_get_hw_accelerated(const char *widget_id);
 extern int widget_service_set_sdk_util(bundle *data);
-
+extern int widget_service_check_db_integrity(bool is_init);
 #ifdef __cplusplus
 }
 #endif
index 7af4acc3de61ee09b868d5ee9a9e376ba7fb230c..c84814bc09e540c00157518be7c1351746eb2318 100644 (file)
@@ -28,6 +28,7 @@ BuildRequires: pkgconfig(cynara-client)
 BuildRequires: pkgconfig(pkgmgr-installer)
 BuildRequires: pkgconfig(ecore-wayland)
 BuildRequires: pkgconfig(iniparser)
+BuildRequires: pkgconfig(libsmack)
 
 %if "%{model_build_feature_widget}" == "0"
 ExclusiveArch:
@@ -50,8 +51,6 @@ cp %{SOURCE1001} .
 
 %build
 
-sqlite3 .widget.db < ./parser/widget.sql
-
 MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
 %cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER}
 make %{?jobs:-j%jobs}
@@ -62,6 +61,7 @@ rm -rf %{buildroot}
 mkdir -p %{buildroot}%{TZ_SYS_DB}
 mkdir -p %{buildroot}%{_sysconfdir}/skel/.applications/dbspace
 
+cat /dev/null > .widget.db
 cat /dev/null > .widget.db-journal
 
 install -m 0644 .widget.db %{buildroot}%{TZ_SYS_DB}
@@ -71,10 +71,7 @@ install -m 0644 .widget.db-journal %{buildroot}%{_sysconfdir}/skel/.applications
 
 %post -n %{name}
 /sbin/ldconfig
-chsmack -a "User::Home" %{TZ_SYS_DB}/.widget.db
-chsmack -a "User::Home" %{TZ_SYS_DB}/.widget.db-journal
-chsmack -a "User::Home" %{_sysconfdir}/skel/.applications/dbspace/.widget.db
-chsmack -a "User::Home" %{_sysconfdir}/skel/.applications/dbspace/.widget.db-journal
+%{_bindir}/widget_recovery init
 
 %postun -n %{name}
 /sbin/ldconfig
@@ -108,6 +105,7 @@ fi
 %{TZ_SYS_DB}/.widget.db
 %{TZ_SYS_DB}/.widget.db-journal
 %{_bindir}/widget_test
+%{_bindir}/widget_recovery
 %{_datarootdir}/widget_service/*
 
 %files devel
diff --git a/parser/widget.sql b/parser/widget.sql
deleted file mode 100644 (file)
index c93fbfe..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-PRAGMA user_version = 40; /* Tizen 4.0 */
-PRAGMA journal_mode = PERSIST;
-PRAGMA foreign_keys = ON;
-
-BEGIN EXCLUSIVE TRANSACTION;
-
-CREATE TABLE widget_class (
-  classid       TEXT NOT NULL,
-  update_period INTEGER DEFAULT 0,
-  setup_appid   TEXT,
-  appid         TEXT NOT NULL,
-  pkgid         TEXT NOT NULL,
-  nodisplay     INTEGER DEFAULT 0,
-  max_instance  INTEGER DEFAULT 0,
-  prime         INTEGER DEFAULT 0,
-  PRIMARY KEY(classid)
-);
-
-CREATE TABLE support_size (
-  classid TEXT NOT NULL,
-  preview TEXT NOT NULL,
-  frame   INTEGER DEFAULT 0,
-  width   INTEGER NOT NULL,
-  height  INTEGER NOT NULL,
-  FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE
-);
-
-CREATE TABLE label (
-  classid TEXT NOT NULL,
-  locale  TEXT DEFAULT 'No Locale',
-  label   TEXT NOT NULL,
-  FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE
-);
-
-CREATE TABLE icon (
-  classid TEXT NOT NULL,
-  locale  TEXT DEFAULT 'No Locale',
-  icon    TEXT NOT NULL,
-  FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE
-);
-
-COMMIT TRANSACTION;
index 406518a932105892f21b703db6a62bbb7cbf9794..a9b963d1b44ab1f45862556c228c7eab6cf573f9 100644 (file)
@@ -25,6 +25,7 @@
 #include <glib.h>
 #include <sqlite3.h>
 
+#include <sys/smack.h>
 #include <vconf.h>
 #include <vconf-keys.h>
 #include <unicode/uloc.h>
@@ -37,6 +38,9 @@
 #include <cynara-client.h>
 #include <stdio.h>
 #include <fcntl.h>
+#include <pwd.h>
+
+#include <aul_widget.h>
 
 #include "widget_errno.h"
 #include "debug.h"
 #define RESOLUTION_SECTION_NAME "resolution"
 #define RESOLUTION_FORMAT "%dx%d"
 
+#define CREATE_WIDGET_TABLE " \
+PRAGMA user_version = 40; \
+PRAGMA journal_mode = PERSIST; \
+PRAGMA foreign_keys = ON; \
+BEGIN EXCLUSIVE TRANSACTION; \
+CREATE TABLE widget_class ( \
+       classid       TEXT NOT NULL, \
+       update_period INTEGER DEFAULT 0, \
+       setup_appid   TEXT, \
+       appid         TEXT NOT NULL, \
+       pkgid         TEXT NOT NULL, \
+       nodisplay     INTEGER DEFAULT 0, \
+       max_instance  INTEGER DEFAULT 0, \
+       prime         INTEGER DEFAULT 0, \
+       PRIMARY KEY(classid) \
+); \
+CREATE TABLE support_size ( \
+       classid TEXT NOT NULL, \
+       preview TEXT NOT NULL, \
+       frame   INTEGER DEFAULT 0, \
+       width   INTEGER NOT NULL, \
+       height  INTEGER NOT NULL, \
+       FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE \
+); \
+CREATE TABLE label ( \
+       classid TEXT NOT NULL, \
+       locale  TEXT DEFAULT 'No Locale', \
+       label   TEXT NOT NULL, \
+       FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE \
+); \
+CREATE TABLE icon ( \
+       classid TEXT NOT NULL, \
+       locale  TEXT DEFAULT 'No Locale', \
+       icon    TEXT NOT NULL, \
+       FOREIGN KEY(classid) REFERENCES widget_class (classid) ON DELETE CASCADE \
+); \
+COMMIT TRANSACTION; "
+
+#define DB_LABEL "User::Home"
+#define SET_SMACK_LABEL(x) \
+do { \
+       if (smack_setlabel((x), DB_LABEL, SMACK_LABEL_ACCESS)) \
+               _E("failed chsmack -a %s %s", DB_LABEL, x); \
+       else \
+               _D("chsmack -a %s %s", DB_LABEL, x); \
+} while (0)
+
+#define WIDGET_TBL_COUNT 4
+static char *_widget_table_list[WIDGET_TBL_COUNT] = {
+       "icon",
+       "label",
+       "support_size",
+       "widget_class"
+};
+
 struct widget_instance_info_s {
        int period;
        bool exists;
@@ -64,6 +123,7 @@ static bool _is_resolution_loaded = false;
 static const char *_iso3lang;
 static char _country[ULOC_COUNTRY_CAPACITY];
 static char *_syslang;
+static bool _is_corrupted = false;
 
 static inline bool _is_widget_feature_enabled(void)
 {
@@ -148,28 +208,218 @@ static int _is_global(uid_t uid)
                return 0;
 }
 
-static const char *_get_db_path(uid_t uid)
+static const char *_get_db_path(uid_t uid, bool is_init)
 {
        const char *path;
 
        if (!_is_global(uid))
                tzplatform_set_user(uid);
 
-       path = tzplatform_mkpath(_is_global(uid) ?
-                       TZ_SYS_DB : TZ_USER_DB, ".widget.db");
+       if (is_init && !_is_global(uid)) {
+               path = tzplatform_mkpath(TZ_SYS_ETC,
+                               "skel/.applications/dbspace/.widget.db");
+       } else {
+               path = tzplatform_mkpath(_is_global(uid) ?
+                               TZ_SYS_DB : TZ_USER_DB, ".widget.db");
+       }
 
+       _E("%s", path);
        tzplatform_reset_user();
 
        return path;
 }
 
+static int _check_integrity_cb(void *pid, int argc, char **argv,
+               char **not_used)
+{
+       if (strcmp(argv[0], "ok") == 0)
+               return 0;
+
+       _E("db integrity result fail : %s" , argv[0]);
+       _is_corrupted = true;
+       return -1;
+}
+
+static int _recover_db(sqlite3 *db, const char *path, uid_t uid)
+{
+       int ret;
+       char *errmsg = NULL;
+       struct passwd pwd;
+       struct passwd *result;
+       char buf[MAX_BUF_SIZE];
+       uid_t new_uid;
+       char journal_path[PATH_MAX];
+
+       _I("DB recovery process start");
+       if (db)
+               sqlite3_close(db);
+       unlink(path);
+
+       ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
+       if (result == NULL) {
+               if (ret == 0)
+                       _E("no such user: %d", uid);
+               else
+                       _E("getpwuid_r failed: %d", errno);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       ret = sqlite3_open_v2(path, &db,
+                       SQLITE_OPEN_CREATE |SQLITE_OPEN_READWRITE,
+                       NULL);
+       if (ret != SQLITE_OK) {
+               _E("Failed to open db[%d]", ret);
+               unlink(path);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       ret = sqlite3_exec(db, CREATE_WIDGET_TABLE, NULL, NULL, &errmsg);
+       sqlite3_close(db);
+       if (ret != SQLITE_OK) {
+               _E("Failed to exec query[%d][%s]", ret, errmsg);
+               sqlite3_free(errmsg);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       snprintf(journal_path, sizeof(journal_path), "%s-journal", path);
+       SET_SMACK_LABEL(path);
+       SET_SMACK_LABEL(journal_path);
+       if (pwd.pw_uid == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER))
+               new_uid = 0;
+       else
+               new_uid = pwd.pw_uid;
+
+       ret = chown(path, new_uid, pwd.pw_gid);
+       if (ret == -1) {
+               _E("chown fail %s", path);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       ret = chown(journal_path, new_uid, pwd.pw_gid);
+       if (ret == -1) {
+               _E("chown fail %s", path);
+               return WIDGET_ERROR_FAULT;
+       }
+       _I("DB recovery process done");
+
+       return WIDGET_ERROR_NONE;
+}
+
+static int _integrity_check(sqlite3 *db)
+{
+       int ret;
+       char *errmsg = NULL;
+
+       ret = sqlite3_exec(db, "PRAGMA integrity_check",
+                              _check_integrity_cb, NULL, &errmsg);
+       if (ret != SQLITE_OK || _is_corrupted) {
+               _E("Failed to exec query[%d][%s]", ret, errmsg);
+               if (errmsg)
+                       sqlite3_free(errmsg);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       return WIDGET_ERROR_NONE;
+}
+
+static int _check_table_exist(sqlite3 *db)
+{
+       int ret;
+       char *val;
+       int idx = 0;
+       sqlite3_stmt *stmt;
+       const char query[] =
+               "SELECT name FROM sqlite_master WHERE type='table'" \
+               " ORDER BY name ASC";
+
+       ret = sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL);
+       if (ret != SQLITE_OK) {
+               /* LCOV_EXCL_START */
+               _E("prepare error: %s", sqlite3_errmsg(db));
+               sqlite3_close_v2(db);
+               return WIDGET_ERROR_FAULT;
+               /* LCOV_EXCL_STOP */
+       }
+
+       while (sqlite3_step(stmt) == SQLITE_ROW && idx < WIDGET_TBL_COUNT) {
+               val = (char *)sqlite3_column_text(stmt, 0);
+               _I("%s : %s", _widget_table_list[idx], val);
+               if (strcmp(_widget_table_list[idx], val) != 0)
+                       continue;
+               idx++;
+       }
+
+       sqlite3_finalize(stmt);
+       if (idx != WIDGET_TBL_COUNT) {
+               _E("wrong table count");
+               sqlite3_close_v2(db);
+               return WIDGET_ERROR_FAULT;
+       }
+
+       return WIDGET_ERROR_NONE;
+}
+
+static int _check_db_integrity(uid_t uid, bool is_init)
+{
+       int ret;
+       const char *path;
+       sqlite3 *db = NULL;
+
+       path = _get_db_path(uid, is_init);
+       if (path == NULL)
+               return WIDGET_ERROR_FAULT;
+
+       /* check table exist */
+       ret = sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, NULL);
+       if (ret != SQLITE_OK) {
+               /* LCOV_EXCL_START */
+               ret = _recover_db(db, path, uid);
+               return ret;
+               /* LCOV_EXCL_STOP */
+       }
+
+       /* check integrity */
+       ret = _integrity_check(db);
+       if (ret != WIDGET_ERROR_NONE) {
+               ret = _recover_db(db, path, uid);
+               return ret;
+       }
+
+       /* check table exist */
+       ret = _check_table_exist(db);
+       if (ret != WIDGET_ERROR_NONE) {
+               ret = _recover_db(db, path, uid);
+               return ret;
+       }
+
+       if (db)
+               sqlite3_close(db);
+
+       return WIDGET_ERROR_NONE;
+}
+
+EAPI int widget_service_check_db_integrity(bool is_init)
+{
+       int ret;
+
+       ret = _check_db_integrity(tzplatform_getuid(TZ_SYS_GLOBALAPP_USER),
+                       is_init);
+       _I("check global app db result : %d", ret);
+
+       ret = _check_db_integrity(tzplatform_getuid(TZ_SYS_DEFAULT_USER),
+                       is_init);
+       _I("check default user db result : %d", ret);
+
+       return WIDGET_ERROR_NONE;
+}
+
 static sqlite3 *_open_db(uid_t uid)
 {
        int ret;
        const char *path;
        sqlite3 *db;
 
-       path = _get_db_path(uid);
+       path = _get_db_path(uid, false);
        if (path == NULL)
                return NULL;
 
index ebc478773539a19ae70d34030fa3f7acc2e177d5..d9b24cfb5d07d356f37ff12dd449d7a928416450 100644 (file)
@@ -2,4 +2,7 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE")
 
 ADD_EXECUTABLE(widget_test widget_test.c)
 TARGET_LINK_LIBRARIES(widget_test ${pkgs_LDFLAGS} ${pkg_extra_LDFLAGS} widget_service "-pie")
+ADD_EXECUTABLE(widget_recovery widget_recovery.c)
+TARGET_LINK_LIBRARIES(widget_recovery ${pkgs_LDFLAGS} ${pkg_extra_LDFLAGS} widget_service "-pie")
 INSTALL(TARGETS widget_test DESTINATION bin)
+INSTALL(TARGETS widget_recovery DESTINATION bin)
diff --git a/tool/widget_recovery.c b/tool/widget_recovery.c
new file mode 100644 (file)
index 0000000..f91f019
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <widget_service_internal.h>
+
+#define ROOT_USER 0
+
+static void print_usage()
+{
+       printf("[usage] widget_recovery <option> ...\n");
+       printf(" - available option list \n\n");
+       printf(" init\n\n");
+       printf(" - end option list\n");
+}
+
+int main(int argc, char **argv)
+{
+       if (getuid() != ROOT_USER) {
+               printf("Only root user can run this tool\n");
+               return -1;
+       }
+
+       if (argc > 1) {
+               if (strcmp(argv[1], "init") != 0) {
+                       print_usage();
+                       exit(0);
+               }
+               widget_service_check_db_integrity(true);
+       } else {
+               widget_service_check_db_integrity(false);
+       }
+       return 0;
+}