#include <glib.h>
#include <libsyscommon/libgdbus.h>
#include <libsyscommon/ini-parser.h>
+#include <device/board-internal.h>
+#include <ctype.h>
#include "log.h"
#include "module-intf.h"
#include "storaged_common.h"
#include "cleanup.h"
+#include "udev.h"
#define MEMORY_STATUS_TMP_PATH "/tmp"
#define MEMORY_STATUS_OPT_PATH "/opt"
#define NEED_CLEANUP_DIR_PATH "/run/storaged/needcleanup"
#define NEED_CLEANUP_FILE_PATH "/run/storaged/needcleanup/trigger"
+#define DM_ROOT_NODE_NAME "rootfs"
+
#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
#define LOW_STORAGE_WARNING "lowstorage_warning"
#define INTERNAL_STORAGE_NOTION "InternalStorageNotiOn"
#define INTERNAL_STORAGE_NOTIOFF "InternalStorageNotiOff"
+#define RECOVERY_ACTION_IGNORE_STR "ignore"
+#define RECOVERY_ACTION_REBOOT_RECOVERY_STR "reboot,recovery"
+#define ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT (1)
+#define ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT (0)
+
enum memnoti_level {
MEMNOTI_LEVEL_FULL = 0,
MEMNOTI_LEVEL_CRITICAL,
double full_level;
};
+enum recovery_action {
+ RECOVERY_ACTION_IGNORE = 0,
+ RECOVERY_ACTION_REBOOT_RECOVERY,
+};
+
+struct storage_config_rootfs_recovery {
+ enum recovery_action action;
+ int dm_verity_corrupted_count;
+ int dm_verity_corrupted_timeout;
+};
+
static guint memnoti_timer;
static int noti_id;
.full_level = MEMNOTI_FULL_VALUE,
};
-static void write_file()
+static char *recovery_action_str[] = {
+ [RECOVERY_ACTION_IGNORE] = RECOVERY_ACTION_IGNORE_STR,
+ [RECOVERY_ACTION_REBOOT_RECOVERY] = RECOVERY_ACTION_REBOOT_RECOVERY_STR,
+};
+static struct storage_config_rootfs_recovery storage_rootfs_recovery_info = {
+ .action = RECOVERY_ACTION_IGNORE,
+ .dm_verity_corrupted_count = ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT,
+ .dm_verity_corrupted_timeout = ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT,
+};
+static guint poweroff_g_timeout_event_source_id = 0;
+
+static void dm_verity_uevent_block_handler(struct udev_device *dev);
+static struct uevent_handler dm_verity_uh = {
+ .subsystem = BLOCK_SUBSYSTEM,
+ .uevent_func = dm_verity_uevent_block_handler,
+};
+
+static void write_file(void)
{
FILE *fp;
static void booting_done(void)
{
static int done;
+ int ret_val;
if (done > 0)
return;
done = 1;
_I("Booting done.");
+ /* UDEV register_udev_uevent_control */
+ ret_val = register_udev_uevent_control(&dm_verity_uh);
+ if (ret_val < 0) {
+ _E("Failed to register dm_verity uevent: %d", ret_val);
+ }
+
if (init_storage_config_info_all() == -1)
_E("Failed to remain mem noti control fd init.");
}
g_source_remove(memnoti_timer);
memnoti_timer = 0;
}
+
+ /* UDEV unregister_udev_uevent_control */
+ unregister_udev_uevent_control(&dm_verity_uh);
}
-static int load_config(struct parse_result *result, void *user_data)
+static gboolean reboot_recovery_callback(void *data)
{
- struct storage_config_info *info = (struct storage_config_info *)user_data;
- char *name;
- char *value;
+ int ret_dbus;
+ gint poweroff_retval;
+
+ GVariant *poweroff_param = g_variant_new("(ss)", "reboot", "recovery");
+
+ ret_dbus = gdbus_call_sync_with_reply_int("org.tizen.system.deviced",
+ "/Org/Tizen/System/DeviceD/PowerOff",
+ "org.tizen.system.deviced.PowerOff",
+ "PowerOffWithOption",
+ poweroff_param, &poweroff_retval);
+ g_variant_unref(poweroff_param);
+
+ if (ret_dbus < 0) {
+ CRITICAL_LOG("Failed to call PowerOffWithOption(reboot recovery): (%d)", ret_dbus);
+ return G_SOURCE_CONTINUE;
+ }
+
+ CRITICAL_LOG("org.tizen.system.deviced.PowerOff.PowerOffWithOption reboot recovery: (%d)",
+ poweroff_retval);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void dm_verity_uevent_block_handler(struct udev_device *dev)
+{
+ static int dm_verity_corrupted_cnt = 0;
+ const char *dm_name = NULL;
+ const char *dm_verity_err_block_nr = NULL;
+ int ret_board_api = 0;
+
+ /* if dm_name is rootfs and DM_VERITY_ERR_BLOCK_NR exist, then reboot recovery */
+ dm_name = udev_device_get_property_value(dev, "DM_NAME");
+ dm_verity_err_block_nr = udev_device_get_property_value(dev, "DM_VERITY_ERR_BLOCK_NR");
+
+ if (!dm_name || !dm_verity_err_block_nr) {
+ return;
+ }
+
+ if(strncmp(DM_ROOT_NODE_NAME, dm_name, strlen(DM_ROOT_NODE_NAME))) {
+ return;
+ }
+
+ ++dm_verity_corrupted_cnt;
+ if (dm_verity_corrupted_cnt < storage_rootfs_recovery_info.dm_verity_corrupted_count) {
+ CRITICAL_LOG("dm_verity_corrupted_cnt: %d(<%d)",
+ dm_verity_corrupted_cnt,
+ storage_rootfs_recovery_info.dm_verity_corrupted_count);
+ return;
+ }
+ /* prevent overflow */
+ dm_verity_corrupted_cnt = storage_rootfs_recovery_info.dm_verity_corrupted_count;
+
+ CRITICAL_LOG("Dmverity corrupted: action=(%s), count=(%d), timeout=(%d)ms",
+ recovery_action_str[storage_rootfs_recovery_info.action],
+ storage_rootfs_recovery_info.dm_verity_corrupted_count,
+ storage_rootfs_recovery_info.dm_verity_corrupted_timeout);
- if (!info)
+ /* action: ignore */
+ if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_IGNORE) {
+ /* ignore, do nothing */
+ return;
+ }
+ /* action: reboot,recovery */
+ if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_REBOOT_RECOVERY &&
+ poweroff_g_timeout_event_source_id <= 0) {
+ /* set current partition status as "corrupted" */
+ ret_board_api = device_board_set_partition_status('\0', "corrupted");
+ CRITICAL_LOG("device_board_set_partition_status: (%d)", ret_board_api);
+ /* clear partition ab cloned */
+ ret_board_api = device_board_clear_partition_ab_cloned();
+ CRITICAL_LOG("device_board_clear_partition_ab_cloned: (%d)", ret_board_api);
+ /* toggle partition */
+ ret_board_api = device_board_switch_partition('\0');
+ CRITICAL_LOG("device_board_switch_partition: (%d)", ret_board_api);
+
+ poweroff_g_timeout_event_source_id =
+ g_timeout_add(storage_rootfs_recovery_info.dm_verity_corrupted_timeout,
+ reboot_recovery_callback, NULL);
+ _I("g_timeout_add event source id: (%u)",
+ poweroff_g_timeout_event_source_id);
+ }
+}
+
+static int config_parse_time_ms(const char *value, int *parsed_ms)
+{
+ char prefix;
+ char *unit_ptr = strchr(value, 's');
+ if (unit_ptr == NULL) {
+ _E("Cannot find 's' in the string (%s)", value);
return -EINVAL;
+ }
- if (!MATCH(result->section, "StorageUsageLevelThreshold"))
+ if (value > (unit_ptr - 1)) {
+ _E("Size of string should be larger than 1");
return -EINVAL;
+ }
+
+ prefix = *(unit_ptr - 1);
+
+ if (isdigit(prefix)) {
+ *parsed_ms = atoi(value) * 1000;
+ return 0;
+ }
+
+ *(unit_ptr - 1) = '\0';
+ if (prefix == 'm') {
+ *parsed_ms = atoi(value);
+ return 0;
+ }
- _D("%s,%s,%s", result->section, result->name, result->value);
+ _E("Unknown unit of time");
+ return -EINVAL;
+}
- name = result->name;
- value = result->value;
+static int load_config(struct parse_result *result, void *user_data)
+{
+ char *name;
+ char *value;
+
+ if (MATCH(result->section, "StorageUsageLevelThreshold")) {
+ _D("%s,%s,%s", result->section, result->name, result->value);
+
+ name = result->name;
+ value = result->value;
+
+ if (MATCH(name, "WarningLevel")) {
+ storage_internal_info.warning_level = (double)atof(value);
+ _I("WarningLevel: (%f)", storage_internal_info.warning_level);
+ } else if (MATCH(name, "CriticalLevel")) {
+ storage_internal_info.critical_level = (double)atof(value);
+ _I("CriticalLevel: (%f)", storage_internal_info.critical_level);
+ } else if (MATCH(name, "FullLevel")) {
+ storage_internal_info.full_level = (double)atof(value);
+ _I("FullLevel: (%f)", storage_internal_info.full_level);
+ }
+
+ } else if (MATCH(result->section, "StorageRootfsRecovery")) {
+ _D("%s,%s,%s", result->section, result->name, result->value);
+
+ name = result->name;
+ value = result->value;
+
+ if (MATCH(name, "DmverityCorruptedAction")) {
+ if (!value || !(*value)) {
+ return -EINVAL;
+ }
+
+ if (!strncmp(value, RECOVERY_ACTION_IGNORE_STR, strlen(RECOVERY_ACTION_IGNORE_STR))) {
+ storage_rootfs_recovery_info.action = RECOVERY_ACTION_IGNORE;
+ } else if (!strncmp(value, RECOVERY_ACTION_REBOOT_RECOVERY_STR, strlen(RECOVERY_ACTION_REBOOT_RECOVERY_STR))) {
+ storage_rootfs_recovery_info.action = RECOVERY_ACTION_REBOOT_RECOVERY;
+ } else {
+ _E("Unknown action: (%s)", value);
+ /* do nothing, use previous value */
+ }
+
+ _I("Dmverity Corrupted Action: (%s)", recovery_action_str[storage_rootfs_recovery_info.action]);
+
+ } else if (MATCH(name, "DmverityCorruptedCount")) {
+ storage_rootfs_recovery_info.dm_verity_corrupted_count = atoi(value);
+ _I("DmverityCorruptedCount: (%d)",
+ storage_rootfs_recovery_info.dm_verity_corrupted_count);
+
+ } else if (MATCH(name, "DmverityCorruptedTimeout")) {
+ int ms = 0;
+ if (!config_parse_time_ms(value, &ms)) {
+ storage_rootfs_recovery_info.dm_verity_corrupted_timeout = ms;
+ }
+ _I("DmverityCorruptedTimeout: (%d)ms",
+ storage_rootfs_recovery_info.dm_verity_corrupted_timeout);
+ }
- if (MATCH(name, "WarningLevel"))
- info->warning_level = (double)atof(value);
- else if (MATCH(name, "CriticalLevel"))
- info->critical_level = (double)atof(value);
- else if (MATCH(name, "FullLevel"))
- info->full_level = (double)atof(value);
+ } else {
+ return -EINVAL;
+ }
return 0;
}
-static void storage_config_load(struct storage_config_info *info)
+static void storage_config_load(void)
{
int ret_val;
- ret_val = config_parse(STORAGE_CONF_FILE, load_config, info);
+ ret_val = config_parse(STORAGE_CONF_FILE, load_config, NULL);
if (ret_val < 0)
_E("Failed to load %s, %d Use default value.", STORAGE_CONF_FILE, ret_val);
}
{
int ret_val;
- storage_config_load(&storage_internal_info);
+ /* UDEV udev_init */
+ udev_init(NULL);
+
+ storage_config_load();
ret_val = gdbus_register_object(NULL, STORAGED_PATH_STORAGE,
&storage_interface);
static void storage_exit(void *data)
{
+ int ret_val;
+
+ udev_exit(NULL);
+
free_cleanup_storage();
/* unregister notifier for below each event */
gdbus_signal_unsubscribe(NULL, id_storage_poweroff);
+
+ /* UDEV unregister_udev_uevent_control */
+ ret_val = unregister_udev_uevent_control(&dm_verity_uh);
+ if (ret_val < 0) {
+ _E("Failed to unregister block uevent: %d", ret_val);
+ }
}
static storaged_module_interface storage_module = {