#define _GNU_SOURCE
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <tzplatform_config.h>
#include <pkgmgr_parser_db.h>
+#include <pkgmgr_parser_db_queries.h>
#ifdef LOG_TAG
#undef LOG_TAG
#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")
+#define NEED_RECOVERY_FILE tzplatform_mkpath(TZ_SYS_DB, ".need_pkg_recovery")
+
+#define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
typedef struct user_info {
uid_t uid;
static GList *user_info_list;
+char *string_trim_inplace(char *s) {
+ char *original = s;
+ size_t len = 0;
+
+ while (isspace((unsigned char) *s))
+ s++;
+
+ if (*s) {
+ char *p = s;
+ while (*p)
+ p++;
+ while (isspace((unsigned char) *(--p)));
+ p[1] = '\0';
+ len = (size_t) (p - s + 1);
+ }
+
+ if (len > MAX_QUERY_LEN)
+ return NULL;
+ return (s == original) ? s : memmove(original, s, len + 1);
+}
+
static char *__get_dbpath(uid_t uid)
{
const char *db_path;
}
}
+static bool __check_aborted(uid_t uid)
+{
+ char buf[BUFSIZE];
+
+ snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
+ if (access(buf, F_OK) == 0) {
+ return true;
+ } else {
+ if (errno != ENOENT)
+ LOGE("failed to access %s, errno: %d", buf, errno);
+ return false;
+ }
+}
+
+static void __create_need_to_recovery_file(uid_t uid)
+{
+ int fd;
+ char buf[BUFSIZE];
+
+ snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
+ fd = open(buf, O_CREAT | O_WRONLY, S_IRWXU);
+ if (fd == -1)
+ LOGE("failed to create file: %s, errno: %d", buf, errno);
+ else
+ close(fd);
+}
+
+static void __remove_need_to_recovery_file(uid_t uid)
+{
+ char buf[BUFSIZE];
+
+ snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
+ if (unlink(buf))
+ LOGE("failed to remove file: %s, errno: %d", buf, errno);
+}
+
static bool __integrity_check(const char *db_path)
{
int ret = -1;
SQLITE_OPEN_READWRITE, NULL);
if (ret != SQLITE_OK) {
LOGE("Failed to open db");
+ sqlite3_close_v2(db);
return false;
}
return false;
}
-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)
- LOGE("no such user: %d", uid);
- else
- LOGE("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)
- LOGE("no such user: %d", uid);
- else
- LOGE("getpwuid_r failed: %d", errno);
- return false;
- }
-
- for (i = 0; i < 2; i++) {
- fd = open(files[i], O_RDONLY);
- if (fd == -1) {
- LOGE("open %s failed: %d", files[i], errno);
- return false;
- }
- ret = fstat(fd, &sb);
- if (ret == -1) {
- LOGE("stat %s failed: %d", files[i], errno);
- close(fd);
- return false;
- }
- if (S_ISLNK(sb.st_mode)) {
- LOGE("%s is symlink!", files[i]);
- close(fd);
- return false;
- }
- ret = fchown(fd, uid, pwd.pw_gid);
- if (ret == -1) {
- LOGE("fchown %s failed: %d", 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) {
- LOGE("fchmod %s failed: %d", 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)) {
- LOGE("Failed to change ownership");
- 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)) {
- LOGE("Failed to change ownership");
- 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)) {
- LOGE("Failed to change ownership");
- return;
- }
- }
-}
-
static void _xsystem(const char *argv[])
{
int status = 0;
const char *initdb_ro[] = { "/usr/bin/pkg_initdb",
"--recover-db", "--keep-db", NULL};
+ __create_need_to_recovery_file(uid);
__create_db(uid);
- __change_permission(uid);
snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
_xsystem((uid > REGULAR_USER) ? initdb_rw : initdb_ro);
+ __remove_need_to_recovery_file(uid);
}
static void __initdb_all()
{
GList *tmp_list = NULL;
user_info *tmp_info;
- __initdb(getuid());
+ __initdb(GLOBAL_USER);
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)
+ if (!tmp_info)
continue;
__initdb(tmp_info->uid);
}
{
GList *tmp_list = NULL;
user_info *tmp_info;
+ bool need_recovery;
for (tmp_list = user_info_list;
tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
+ need_recovery = false;
tmp_info = (user_info *)tmp_list->data;
if (!tmp_info)
continue;
if (!__integrity_check(tmp_info->db_path)) {
LOGE("User db for %d has corrupted", (int)tmp_info->uid);
- __initdb(tmp_info->uid);
+ need_recovery = true;
+ } else if (__check_aborted(tmp_info->uid)) {
+ need_recovery = true;
}
+
+ if (need_recovery)
+ __initdb(tmp_info->uid);
}
}
snprintf(traverse_path, sizeof(traverse_path), "%s/user", db_path);
dir = opendir(traverse_path);
+ if (!dir) {
+ LOGE("Failed to open dir: %d (%s)", errno, traverse_path);
+ return;
+ }
while ((ent = readdir(dir)) != NULL) {
- snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
+ ret = snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
ent->d_name);
+ if (ret < 0 || ret > PATH_MAX) {
+ LOGE("snprintf fail");
+ closedir(dir);
+ return;
+ }
ret = stat(abs_dirname, &stats);
if (ret != 0) {
if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name) ||
!S_ISDIR(stats.st_mode))
continue;
+ uid = (uid_t)atoi(ent->d_name);
+ if (uid < REGULAR_USER)
+ 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);
+ user_info_list = g_list_prepend(user_info_list, info);
}
closedir(dir);
}
+static bool __check_db_schema(const char *db_path,
+ const char **db_tables, const char **init_queries) {
+ int ret = -1;
+ int i;
+ sqlite3_stmt *stmt = NULL;
+ const char *check_result;
+ char *schema_in_library, *schema_in_db;
+ static const char table_schema_query[] =
+ "SELECT sql from sqlite_master WHERE name=?";
+ sqlite3 *db;
+ char *schema_lib_tmp;
+ char *schema_lib_ptr;
+ char *schema_db_tmp;
+ char *schema_db_ptr;
+ char *line_db;
+ char *line_library;
+
+ ret = sqlite3_open_v2(db_path, &db,
+ SQLITE_OPEN_READONLY, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("Failed to open db");
+ sqlite3_close_v2(db);
+ return false;
+ }
+
+ ret = sqlite3_busy_handler(db, __db_busy_handler, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("failed to register busy handler: %s",
+ sqlite3_errmsg(db));
+ sqlite3_close_v2(db);
+ return ret;
+ }
+ for (i = 0; db_tables[i] != NULL; i++) {
+ ret = sqlite3_prepare_v2(db, table_schema_query,
+ strlen(table_schema_query), &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("Failed to check db schema : %s",
+ sqlite3_errmsg(db));
+ goto err;
+ }
+
+ ret = sqlite3_bind_text(stmt, 1,
+ db_tables[i],-1, SQLITE_STATIC);
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ LOGE("Failed to check db schema :%s",
+ sqlite3_errmsg(db));
+ goto err;
+ }
+
+ check_result = (const char *)sqlite3_column_text(stmt, 0);
+ if (!check_result) {
+ LOGE("Failed to check db schema :%s",
+ sqlite3_errmsg(db));
+ goto err;
+ }
+
+ schema_in_library = strstr(init_queries[i], db_tables[i]);
+ if (schema_in_library == NULL) {
+ LOGE("Failed to get initialization query from library");
+ goto err;
+ }
+ schema_lib_tmp = strdup(schema_in_library);
+ schema_lib_ptr = schema_lib_tmp;
+ if (schema_lib_tmp == NULL) {
+ LOGE("Out of memory");
+ goto err;
+ }
+
+ schema_in_db = strstr(check_result, db_tables[i]);
+ if (schema_in_db == NULL) {
+ LOGE("Failed to get initialization query from db");
+ free(schema_lib_ptr);
+ goto err;
+ }
+ schema_db_tmp = strdup(schema_in_db);
+ schema_db_ptr = schema_db_tmp;
+ if (schema_db_tmp == NULL) {
+ LOGE("Out of memory");
+ free(schema_lib_ptr);
+ goto err;
+ }
+
+ while (true) {
+ line_db = strsep(&schema_db_tmp, ",");
+ line_library = strsep(&schema_lib_tmp, ",");
+ if (line_db == NULL || line_library == NULL)
+ break;
+
+ if (string_trim_inplace(line_db) == NULL ||
+ string_trim_inplace(line_library) == NULL)
+ break;
+
+ ret = strcmp(string_trim_inplace(line_db),
+ string_trim_inplace(line_library));
+ if (ret != 0)
+ break;
+ }
+
+ free(schema_lib_ptr);
+ free(schema_db_ptr);
+
+ if (ret != 0) {
+ LOGE("Broken schema detected in table[%s], query[%s]",
+ db_tables[i], schema_in_db);
+ goto err;
+ }
+
+ sqlite3_finalize(stmt);
+ stmt = NULL;
+ }
+
+ sqlite3_close(db);
+ return true;
+
+err:
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return false;
+}
+
static void _check_db()
{
+ int need_recovery = false;
+
if (!__integrity_check(PKGMGR_CERT_DB_FILE)) {
LOGE("Cert db corrupted. restore entire pkgmgr db");
- __initdb_all();
- return;
+ need_recovery = true;
} else if (!__integrity_check(PKGMGR_PARSER_DB_FILE)) {
LOGE("Global parser db corrupted. restore entire pkgmgr db");
+ need_recovery = true;
+ } else if (__check_aborted(GLOBAL_USER)) {
+ LOGE("Previous recovery was aborted. restore entire pkgmgr db");
+ need_recovery = true;
+ } else if (!__check_db_schema(PKGMGR_PARSER_DB_FILE,
+ PARSER_TABLES, PARSER_INIT_QUERIES)) {
+ LOGE("Global parser db schema was broken. restore entire pkgmgr db");
+ need_recovery = true;
+ } else if (!__check_db_schema(PKGMGR_CERT_DB_FILE,
+ CERT_TABLES, CERT_INIT_QUERIES)) {
+ LOGE("Cert db schema was broken. restore entire pkgmgr db");
+ need_recovery = true;
+ }
+
+ if (need_recovery) {
__initdb_all();
return;
}