Detect dm-verity corruption of rootfs using udev and do action in conf file 34/274434/11 accepted/tizen/unified/20220526.144108 submit/tizen/20220525.001052
authorSangYoun Kwak <sy.kwak@samsung.com>
Thu, 28 Apr 2022 08:29:07 +0000 (17:29 +0900)
committerSangYoun Kwak <sy.kwak@samsung.com>
Tue, 10 May 2022 09:08:11 +0000 (18:08 +0900)
Read StorageRootfsRecovery section from storage.conf
StorageRootfsRecovery contains
DmverityCorruptedAction, DmverityCorruptedCount, DmverityCorruptedTimeout
Use CRITICAL_LOG when rootfs(dm-verity) corrupted

Change-Id: Id5a01d89e50acd9f487f8e77c157915cce83700a
Signed-off-by: SangYoun Kwak <sy.kwak@samsung.com>
CMakeLists.txt
conf/storage.conf
packaging/storaged.spec
src/shared/log.h
src/storage/CMakeLists.txt
src/storage/storage.c

index 74162b2..6577dd9 100644 (file)
@@ -29,6 +29,9 @@ SET(PKG_MODULES
 
 ADD_DEFINITIONS("-DLIBPATH=\"${LIB_INSTALL_DIR}\"")
 ADD_DEFINITIONS("-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64")
+IF(CRITICAL_LOG_MODULE STREQUAL on)
+       ADD_DEFINITIONS("-DCRITICAL_LOG_ON")
+ENDIF()
 
 INCLUDE(FindPkgConfig)
 pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED ${PKG_MODULES})
index 188f5e8..6ccb09c 100644 (file)
@@ -5,3 +5,8 @@ WarningLevel=5
 CriticalLevel=0.1
 #0.0%
 FullLevel=0
+
+[StorageRootfsRecovery]
+#DmverityCorruptedAction=reboot,recovery
+#DmverityCorruptedCount=3
+#DmverityCorruptedTimeout=10s
index 0b2a5c8..6c6a3ae 100644 (file)
@@ -110,6 +110,7 @@ This package can be installed optional for auto dbus test.
        -DBLOCK_TMPFS=on \
        -DSTORAGE_MODULE=on \
        -DEXTENDED_STORAGE=%{extended_storage} \
+       -DCRITICAL_LOG_MODULE=on \
        #eol
 
 %build
index d9da05d..77cb86d 100644 (file)
 #undef LOG_TAG
 #define LOG_TAG "STORAGED"
 
+/* define CRITICAL_LOG */
+#ifdef CRITICAL_LOG_ON
+#define CRITICAL_LOG(fmt, arg...) \
+       do { CRITICAL_LOG_(LOG_ID_SYSTEM, DLOG_INFO, LOG_TAG, fmt, ##arg); } while (0)
+#else
+#define CRITICAL_LOG(fmt, arg...)       _I(fmt, ##arg)
+#endif
+
 #define _D(fmt, arg...) \
        do { SLOGD(fmt, ##arg); } while (0)
 #define _I(fmt, arg...) \
index 4e8c38b..736a9a7 100644 (file)
@@ -10,6 +10,8 @@ pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED
                storage
                vconf
                libsyscommon
+               libudev
+               capi-system-device
 )
 
 FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS})
@@ -28,6 +30,7 @@ SET(SRCS ${ALL_SRCS})
 SET(SHARED_SRCS
        ../shared/common.c
        ../shared/fd_handler.c
+       ../shared/udev.c
 )
 
 ADD_LIBRARY(${PROJECT_NAME} ${SRCS} ${SHARED_SRCS})
index 7d21b7f..8c8bb7a 100644 (file)
 #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"
@@ -60,6 +63,8 @@
 #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,
@@ -96,6 +106,17 @@ struct storage_config_info {
        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;
 
@@ -125,7 +146,24 @@ static struct storage_config_info storage_opt_info = {
        .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;
 
@@ -634,12 +672,19 @@ static dbus_interface_u storage_interface = {
 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.");
 }
@@ -656,40 +701,197 @@ static void storage_poweroff(GDBusConnection  *conn,
                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);
 }
@@ -698,7 +900,10 @@ static void storage_init(void *data)
 {
        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);
@@ -724,9 +929,19 @@ static void storage_init(void *data)
 
 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 = {