Supplement db recovery tool 78/181778/15
authorJunghyun Yeon <jungh.yeon@samsung.com>
Mon, 18 Jun 2018 08:12:14 +0000 (17:12 +0900)
committerJunghyun Yeon <jungh.yeon@samsung.com>
Mon, 16 Jul 2018 02:43:42 +0000 (11:43 +0900)
- Recovery tool now checks db status with integrity_check command.
- When tool detects corruption, invoke pkg_initdb to restore it.

Related changes:
[app-installers] : https://review.tizen.org/gerrit/182176
[tpk-backend] : https://review.tizen.org/gerrit/182177

Change-Id: Ide052f512d0e052fe871d7c61d1fd29bf5b33cdc
Signed-off-by: Junghyun Yeon <jungh.yeon@samsung.com>
packaging/pkgmgr-info.manifest
packaging/pkgmgr-info.spec
parser/CMakeLists.txt
tool/CMakeLists.txt
tool/pkg-db-creator.c [new file with mode: 0644]
tool/pkg-db-recovery.c
tool/pkg-db-recovery.service

index 017d22d..495d579 100644 (file)
@@ -2,4 +2,8 @@
  <request>
     <domain name="_"/>
  </request>
+ <assign>
+    <filesystem path="/usr/bin/pkg-db-recovery" label="System" exec_label="none" />
+    <filesystem path="/usr/bin/pkg-db-creator" label="System" exec_label="User::Home" />
+ </assign>
 </manifest>
index 613f9bf..ce925ab 100644 (file)
@@ -74,6 +74,7 @@ ln -sf ../pkg-db-recovery.service %{buildroot}%{_unitdir}/multi-user.target.want
 %defattr(-,root,root,-)
 %{_libdir}/libpkgmgr-info.so.*
 %{_bindir}/pkg-db-recovery
+%{_bindir}/pkg-db-creator
 %{_unitdir}/pkg-db-recovery.service
 %{_unitdir}/multi-user.target.wants/pkg-db-recovery.service
 
index 4fff4c5..10bb4ff 100644 (file)
@@ -53,6 +53,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
 # This library is for installer backend
 AUX_SOURCE_DIRECTORY(src PARSER_SRCS)
 add_library(pkgmgr_parser SHARED ${PARSER_SRCS})
+
 set_target_properties(pkgmgr_parser PROPERTIES SOVERSION ${MAJORVER})
 set_target_properties(pkgmgr_parser PROPERTIES VERSION ${FULLVER})
 set_target_properties(pkgmgr_parser PROPERTIES COMPILE_FLAGS "${parser_pkgs_CFLAGS_str}")
index 7207860..b040a1b 100644 (file)
@@ -7,8 +7,9 @@ SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
 
 ### Get required CFLAGS, LDFLAGS from pkg-config
 SET(PKG_DB_RECOVERY pkg-db-recovery)
+SET(PKG_DB_CREATOR "pkg-db-creator")
 include(FindPkgConfig)
-pkg_check_modules(TOOL_DEPS REQUIRED sqlite3 libtzplatform-config)
+pkg_check_modules(TOOL_DEPS REQUIRED glib-2.0 sqlite3 libtzplatform-config)
 
 FOREACH(FLAG ${TOOL_DEPS_CFLAGS})
        SET(${CMAKE_C_FLAGS} "${CMAKE_C_FLAGS} ${FLAG}")
@@ -20,8 +21,11 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
 
 ### Build
 ADD_EXECUTABLE(${PKG_DB_RECOVERY} ${CMAKE_CURRENT_SOURCE_DIR}/${PKG_DB_RECOVERY}.c)
-TARGET_LINK_LIBRARIES(${PKG_DB_RECOVERY} ${TOOL_DEPS_LDFLAGS} "-pie")
+TARGET_LINK_LIBRARIES(${PKG_DB_RECOVERY} pkgmgr_parser pkgmgr-info ${TOOL_DEPS_LDFLAGS} "-pie")
+ADD_EXECUTABLE(${PKG_DB_CREATOR} ${CMAKE_CURRENT_SOURCE_DIR}/${PKG_DB_CREATOR}.c)
+TARGET_LINK_LIBRARIES(${PKG_DB_CREATOR} pkgmgr_parser pkgmgr-info ${TOOL_DEPS_LDFLAGS} "-pie")
 
 ## Install
 INSTALL(TARGETS ${PKG_DB_RECOVERY} DESTINATION bin)
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PKG_DB_RECOVERY}.service DESTINATION ${UNITDIR})
+INSTALL(TARGETS ${PKG_DB_CREATOR} DESTINATION bin)
diff --git a/tool/pkg-db-creator.c b/tool/pkg-db-creator.c
new file mode 100644 (file)
index 0000000..ba2026d
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Junghyun Yeon <jungh.yeon@samsung.com>,
+ * Sangyoon Jang <s89.jang@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <tzplatform_config.h>
+
+#include "pkgmgr-info.h"
+#include "pkgmgr_parser_db.h"
+
+#ifndef OWNER_ROOT
+#define OWNER_ROOT 0
+#endif
+#ifndef GLOBAL_USER
+#define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
+#endif
+
+static int _remove_file(const char *path)
+{
+       if (!path)
+               return 0;
+
+       if (access(path, F_OK) != 0)
+               return 0;
+
+       return remove(path);
+}
+
+static int _remove_db(uid_t uid)
+{
+       char *cert_db;
+       char *parser_db;
+       char journal_path[PATH_MAX];
+
+       parser_db = getUserPkgParserDBPathUID(uid);
+       if (!parser_db) {
+               printf("Failed to get parser db path\n");
+               return -1;
+       }
+
+       if (_remove_file(parser_db) != 0) {
+               printf("Failed to remove parser db[%s]\n", parser_db);
+               free(parser_db);
+               return -1;
+       }
+
+       snprintf(journal_path, sizeof(journal_path), "%s-journal", parser_db);
+       free(parser_db);
+       if (_remove_file(journal_path) != 0) {
+               printf("Failed to remove journal[%s]\n", journal_path);
+               return -1;
+       }
+
+       if (uid != OWNER_ROOT && uid != GLOBAL_USER)
+               return 0;
+
+       cert_db = getUserPkgCertDBPath();
+       if (!cert_db) {
+               printf("Failed to get cet db path\n");
+               return -1;
+       }
+
+       if (_remove_file(cert_db) != 0) {
+               printf("Failed to remove parser db[%s]\n", cert_db);
+               free(cert_db);
+               return -1;
+       }
+
+       snprintf(journal_path, sizeof(journal_path), "%s-journal", cert_db);
+       free(cert_db);
+       if (_remove_file(journal_path) != 0) {
+               printf("Failed to remove journal[%s]\n", journal_path);
+               return -1;
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int uid;
+       int ret = 0;
+
+       if ((int)getuid() > OWNER_ROOT) {
+               printf("This cmd is not allowed for regular user\n");
+               return -1;
+       }
+
+       if (argc == 1) {
+               printf("argument should be provided\n");
+               return -1;
+       }
+
+       uid = atoi(argv[1]);
+       ret = _remove_db((uid_t)uid);
+       if (ret != 0)
+               printf("failed to remove database for uid[%d]\n", uid);
+
+       ret = pkgmgr_parser_initialize_parser_db((uid_t)uid);
+       if (ret != 0) {
+               printf("failed to create parser db for uid [%d], err[%d]\n", uid, ret);
+               return -1;
+
+       }
+
+       if (uid == 0) {
+               ret = pkgmgr_parser_initialize_cert_db();
+               if (ret != 0) {
+                       printf("failed to create cert db for uid [%d], err[%d]\n", uid, ret);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
index 715d48a..58e41e1 100644 (file)
 
 #define _GNU_SOURCE
 
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
+#include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <unistd.h>
 
 #include <sqlite3.h>
 #include <tzplatform_config.h>
 
+#include <pkgmgr_parser_db.h>
+
+#ifndef APPFW_USER
+#define APPFW_USER "app_fw"
+#endif
+#ifndef OWNER_ROOT
+#define OWNER_ROOT 0
+#endif
 #define REGULAR_USER 5000
 #define MAX_QUERY_LEN  4096
+#define BUFSIZE 4096
 
 #define PKGMGR_PARSER_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_parser.db")
 #define PKGMGR_CERT_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_cert.db")
 
-static int __rollback_db(sqlite3 *db, const char *table_name)
+typedef struct user_info {
+       uid_t uid;
+       char *db_path;
+} user_info;
+
+static GList *user_info_list;
+
+static char *__get_dbpath(uid_t uid)
 {
-       static const char rollback_query_raw[] =
-                       "SELECT COUNT(*) FROM %s WHERE 1=0";
-       int ret;
-       sqlite3_stmt *stmt = NULL;
-       char query[MAX_QUERY_LEN] = { '\0' };
+       const char *db_path;
+       char path[PATH_MAX];
 
-       sqlite3_snprintf(MAX_QUERY_LEN, query, rollback_query_raw, table_name);
-       ret = sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL);
-       if (ret != SQLITE_OK) {
-               printf("Failed to recover db : %s\n", sqlite3_errmsg(db));
-               return -1;
+       db_path = tzplatform_getenv(TZ_SYS_DB);
+       if (db_path == NULL) {
+               printf("Failed to get TZ_SYS_DB path");
+               return NULL;
        }
-       sqlite3_finalize(stmt);
-       return 0;
+
+       snprintf(path, sizeof(path), "%s/user/%d/.pkgmgr_parser.db", db_path, uid);
+       return strdup(path);
 }
 
-static int __check_and_rollback_db(const char *db_path, const char *table_name)
+static bool __integrity_check(const char *db_path)
 {
        int ret = -1;
+       sqlite3_stmt *stmt = NULL;
+       const char *check_result;
+       static const char integrity_check_query[] =
+                       "PRAGMA integrity_check";
        sqlite3 *db;
 
        ret = sqlite3_open_v2(db_path, &db,
                        SQLITE_OPEN_READWRITE, NULL);
        if (ret != SQLITE_OK) {
                printf("Failed to open db\n");
-               return -1;
+               return false;
+       }
+
+       ret = sqlite3_prepare_v2(db, integrity_check_query,
+                       strlen(integrity_check_query), &stmt, NULL);
+       if (ret != SQLITE_OK) {
+               printf("Failed to check integrity_check : %s\n", sqlite3_errmsg(db));
+               goto err;
        }
 
-       ret = __rollback_db(db, table_name);
-       if (ret != 0)
-               printf("Failed to rollback db\n");
+       ret = sqlite3_step(stmt);
+       if (ret != SQLITE_ROW) {
+               printf("Failed to check integrity db :%s\n", sqlite3_errmsg(db));
+               goto err;
+       }
+
+       check_result = (const char *)sqlite3_column_text(stmt, 0);
+       if (!check_result) {
+               printf("Failed to check integrity db :%s\n", sqlite3_errmsg(db));
+               goto err;
+       }
+
+       if (strcasecmp(check_result, "ok") != 0) {
+               printf("db corrupted :%s\n", db_path);
+               goto err;
+       }
 
+       sqlite3_finalize(stmt);
+       sqlite3_close(db);
+       return true;
+
+err:
+       sqlite3_finalize(stmt);
        sqlite3_close(db);
-       return ret;
+       return false;
 }
 
-static void _check_db()
+static bool __change_owner(const char *files[2], uid_t uid)
 {
        int ret;
+       int i;
+       int fd;
+       struct passwd pwd;
+       struct passwd *result;
+       char buf[BUFSIZE];
+       struct stat sb;
+       mode_t mode;
+
+       if (uid == OWNER_ROOT) {
+               ret = getpwnam_r(APPFW_USER, &pwd, buf, sizeof(buf), &result);
+               if (result == NULL) {
+                       if (ret == 0)
+                               printf("no such user: %d\n", uid);
+                       else
+                               printf("getpwnam_r failed: %d", errno);
+                       return false;
+               }
+               uid = pwd.pw_uid;
+       }
+
+       ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
+       if (result == NULL) {
+               if (ret == 0)
+                       printf("no such user: %d\n", uid);
+               else
+                       printf("getpwuid_r failed: %d\n", errno);
+               return false;
+       }
+
+       for (i = 0; i < 2; i++) {
+               fd = open(files[i], O_RDONLY);
+               if (fd == -1) {
+                       printf("open %s failed: %d\n", files[i], errno);
+                       return false;
+               }
+               ret = fstat(fd, &sb);
+               if (ret == -1) {
+                       printf("stat %s failed: %d\n", files[i], errno);
+                       close(fd);
+                       return false;
+               }
+               if (S_ISLNK(sb.st_mode)) {
+                       printf("%s is symlink!\n", files[i]);
+                       close(fd);
+                       return false;
+               }
+               ret = fchown(fd, uid, pwd.pw_gid);
+               if (ret == -1) {
+                       printf("fchown %s failed: %d\n", files[i], errno);
+                       close(fd);
+                       return false;
+               }
+
+               mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+               if (strstr(files[0], PKGMGR_CERT_DB_FILE) != NULL)
+                       mode |= S_IWOTH;
+               ret = fchmod(fd, mode);
+               if (ret == -1) {
+                       printf("fchmod %s failed: %d\n", files[i], errno);
+                       close(fd);
+                       return false;;
+               }
+               close(fd);
+       }
+
+       return true;
+}
+
+static void __change_permission(uid_t uid)
+{
+       const char *files[2];
+       char *parser_dbpath = NULL;
+       char journal_file[PATH_MAX];
+       GList *tmp_list;
+       user_info *tmp_info;
+
+       if (uid < REGULAR_USER) {
+               files[0] = PKGMGR_PARSER_DB_FILE;
+               snprintf(journal_file, sizeof(journal_file),
+                               "%s-journal", PKGMGR_PARSER_DB_FILE);
+               files[1] = journal_file;
+
+               if (!__change_owner(files, uid)) {
+                       printf("Failed to change ownership\n");
+                       return;
+               }
+
+               files[0] = PKGMGR_CERT_DB_FILE;
+               snprintf(journal_file, sizeof(journal_file),
+                               "%s-journal", PKGMGR_CERT_DB_FILE);
+               files[1] = journal_file;
+               if (!__change_owner(files, uid)) {
+                       printf("Failed to change ownership\n");
+                       return;
+               }
+       } else {
+               for (tmp_list = user_info_list; tmp_list != NULL;
+                               tmp_list = g_list_next(tmp_list)) {
+                       tmp_info = (user_info *)tmp_list->data;
+                       if (!tmp_info)
+                               continue;
+
+                       if (tmp_info->uid == uid) {
+                               parser_dbpath = tmp_info->db_path;
+                               break;
+                       }
+               }
+               files[0] = parser_dbpath;
+               snprintf(journal_file, sizeof(journal_file),
+                               "%s-journal", parser_dbpath);
+               files[1] = journal_file;
+
+               if (!__change_owner(files, uid)) {
+                       printf("Failed to change ownership\n");
+                       return;
+               }
+       }
+}
+
+static void _xsystem(const char *argv[])
+{
+       int status = 0;
+       pid_t pid;
+
+       pid = fork();
+       switch (pid) {
+       case -1:
+               perror("fork failed");
+               return;
+       case 0:
+               /* child */
+               execvp(argv[0], (char *const *)argv);
+               _exit(-1);
+       default:
+               /* parent */
+               break;
+       }
+       if (waitpid(pid, &status, 0) == -1) {
+               perror("waitpid failed");
+               return;
+       }
+       if (WIFSIGNALED(status)) {
+               perror("signal");
+               return;
+       }
+       if (!WIFEXITED(status)) {
+               /* shouldn't happen */
+               perror("should not happen");
+               return;
+       }
+}
+
+static void __create_db(uid_t uid)
+{
+       char uid_string[11];
+       const char *create_db[] = { "/usr/bin/pkg-db-creator", uid_string, NULL};
+
+       snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
+       _xsystem(create_db);
+}
 
-       ret = __check_and_rollback_db(PKGMGR_PARSER_DB_FILE, "package_info");
-       if (ret != 0)
-               printf("Failed to check and rollback parser db\n");
+static void __initdb(uid_t uid)
+{
+       char uid_string[11];
+       const char *initdb_rw[] = { "/usr/bin/pkg_initdb",
+                       "--recover-db", "--uid", uid_string, "--keep-db", NULL};
+       const char *initdb_ro[] =  { "/usr/bin/pkg_initdb",
+                       "--recover-db", "--keep-db", NULL};
 
-       ret = __check_and_rollback_db(PKGMGR_CERT_DB_FILE, "package_cert_info");
-       if (ret != 0)
-               printf("Failed to check and rollback cert db\n");
+       __create_db(uid);
+       __change_permission(uid);
+       snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
+       _xsystem((uid > REGULAR_USER) ? initdb_rw : initdb_ro);
 }
 
+static void __initdb_all()
+{
+       GList *tmp_list = NULL;
+       user_info *tmp_info;
+       __initdb(getuid());
+
+       for (tmp_list = user_info_list;
+                       tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
+               tmp_info = (user_info *)tmp_list->data;
+               if (!tmp_info || tmp_info->uid < REGULAR_USER)
+                       continue;
+               __initdb(tmp_info->uid);
+       }
+}
+
+static void __check_user_db()
+{
+       GList *tmp_list = NULL;
+       user_info *tmp_info;
+
+       for (tmp_list = user_info_list;
+                       tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
+               tmp_info = (user_info *)tmp_list->data;
+               if (!tmp_info)
+                       continue;
+               if (!__integrity_check(tmp_info->db_path)) {
+                       printf("User db for %d has corrupted\n", (int)tmp_info->uid);
+                       __initdb(tmp_info->uid);
+               }
+       }
+}
+
+static void _free_user_info(gpointer data)
+{
+       user_info *info = (user_info *)data;
+       free(info->db_path);
+       free(info);
+}
+
+static void _get_user_list()
+{
+       DIR *dir;
+       struct dirent *ent;
+       struct stat stats;
+       char traverse_path[PATH_MAX];
+       char abs_dirname[PATH_MAX];
+       const char *db_path;
+       uid_t uid;
+       user_info *info;
+
+       db_path = tzplatform_getenv(TZ_SYS_DB);
+       if (db_path == NULL) {
+               printf("Failed to get TZ_SYS_DB path");
+               return;
+       }
+
+       snprintf(traverse_path, sizeof(traverse_path), "%s/user", db_path);
+       dir = opendir(traverse_path);
+
+       while ((ent = readdir(dir)) != NULL) {
+               snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
+                        ent->d_name);
+               stat(abs_dirname, &stats);
+               if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name) ||
+                               !S_ISDIR(stats.st_mode))
+                       continue;
+               info = calloc(1, sizeof(user_info));
+               if (!info) {
+                       closedir(dir);
+                       return;
+               }
+
+               uid = (uid_t)atoi(ent->d_name);
+               if (!uid) {
+                       free(info);
+                       continue;
+               }
+               info->uid = uid;
+               info->db_path = __get_dbpath(uid);
+               user_info_list = g_list_append(user_info_list, info);
+       }
+
+       closedir(dir);
+}
+
+static void _check_db()
+{
+       if (!__integrity_check(PKGMGR_CERT_DB_FILE)) {
+               printf("Cert db corrupted. restore entire pkgmgr db\n");
+               __initdb_all();
+               return;
+       } else if (!__integrity_check(PKGMGR_PARSER_DB_FILE)) {
+               printf("Global parser db corrupted. restore entire pkgmgr db\n");
+               __initdb_all();
+               return;
+       }
+
+       __check_user_db();
+}
 
 int main(int argc, char *argv[])
 {
@@ -93,7 +409,9 @@ int main(int argc, char *argv[])
                return -1;
        }
 
+       _get_user_list();
        _check_db();
 
+       g_list_free_full(user_info_list, _free_user_info);
        return 0;
 }
index 9b293e0..3a7bc35 100644 (file)
@@ -4,8 +4,8 @@ DefaultDependencies=false
 Before=ac.service
 
 [Service]
-User=app_fw
-Group=app_fw
+User=root
+Group=root
 SmackProcessLabel=System
 ExecStart=/usr/bin/pkg-db-recovery