Add system-recovery binary for USB recovery 71/238571/4
authorKichan Kwon <k_c.kwon@samsung.com>
Tue, 14 Jul 2020 07:30:07 +0000 (16:30 +0900)
committerKichan Kwon <k_c.kwon@samsung.com>
Thu, 16 Jul 2020 02:18:21 +0000 (11:18 +0900)
- Find recovery image on USB storage
- Fuse images to the appropriate device
  refering to config file
  - Config file is included in recovery image
  - Config file format : [LABEL] [IMG_BASENAME] [DEVICE_PATH]

Change-Id: I7e2d3d3dd6f04c273179b267c6cb611a1ed6542e
Signed-off-by: Kichan Kwon <k_c.kwon@samsung.com>
CMakeLists.txt
packaging/system-recovery.spec
scripts/recovery-init.in
src/CMakeLists.txt [new file with mode: 0644]
src/system-recovery.c [new file with mode: 0644]
src/system-recovery.h.in [new file with mode: 0644]

index 5fb9e13..90f2068 100644 (file)
@@ -4,8 +4,12 @@ PROJECT(system-recovery)
 SET(INITRD_RECOVERY_LIBEXEC_DIR ${LIBEXECDIR}/initrd-recovery)
 SET(SYSTEM_RECOVERY_LIBEXEC_DIR ${LIBEXECDIR}/system-recovery)
 
+SET(USB_MOUNTPOINT_ROOT ${TZ_SYS_STORAGE})
+SET(USB_MOUNTPOINT_PREFIX USBDrive)
+
 SET(PKGS
        scripts
+       src
 )
 
 FOREACH(PKG ${PKGS})
index c858dea..0954a5e 100644 (file)
@@ -9,6 +9,7 @@ Source0:        %{name}-%{version}.tar.gz
 Source1001:     system-recovery.manifest
 
 BuildRequires:  cmake
+BuildRequires:  pkgconfig(libtzplatform-config)
 
 Requires:       initrd-recovery
 
@@ -21,7 +22,8 @@ System recovery package
 cp %{SOURCE1001} .
 
 %build
-%cmake . -DLIBEXECDIR=%{_libexecdir}
+%cmake . -DLIBEXECDIR=%{_libexecdir}   \
+                -DTZ_SYS_STORAGE=%{TZ_SYS_STORAGE}
 
 %install
 %make_install
index 88ab7b4..9809594 100644 (file)
@@ -26,7 +26,7 @@ get_partition_id() {
 #------------------------------------------------
 #       mount_usb_partitions
 #------------------------------------------------
-USB_MOUNTPOINT_PREFIX=${FAKE_ROOT}/opt/media/USBDrive
+USB_MOUNTPOINT_PREFIX=${FAKE_ROOT}@USB_MOUNTPOINT_ROOT@/@USB_MOUNTPOINT_PREFIX@
 mount_usb_partitions() {
     echo "mount USB partitions"
     DEVICE_TYPE_LIST=(
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8094d5f
--- /dev/null
@@ -0,0 +1,26 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(PKG_NAME "system-recovery")
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PKG_NAME}.h.in ${CMAKE_CURRENT_SOURCE_DIR}/${PKG_NAME}.h @ONLY)
+
+# Set build flag
+SET(PKG_MODULES
+)
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${PKG_NAME} REQUIRED ${PKG_MODULES})
+
+FOREACH(flag ${${PKG_NAME}_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS_BASE "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror -fvisibility=hidden")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_BASE} -Wall -fPIE")
+FILE(GLOB SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
+ADD_EXECUTABLE(${PKG_NAME} ${SOURCE_FILES})
+TARGET_LINK_LIBRARIES(${PKG_NAME} ${${PKG_NAME}_LDFLAGS} "-ldl")
+INSTALL(TARGETS ${PKG_NAME} DESTINATION ${SYSTEM_RECOVERY_LIBEXEC_DIR})
diff --git a/src/system-recovery.c b/src/system-recovery.c
new file mode 100644 (file)
index 0000000..e5fca3d
--- /dev/null
@@ -0,0 +1,235 @@
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libgen.h>
+#include <linux/loop.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "system-recovery.h"
+
+
+static char recovery_image_path[PATH_MAX - FIELD_LENGTH];
+
+
+#ifdef LOG_FILE
+/**
+ * Log is written to the buffer and written to the USB
+ * before termination because of these issues;
+ *
+ * - We can't write to file before finding USB mountpoint
+ * - Mounting same place twice is not allowed : log and recovery image
+ */
+
+char log_buf[8192];
+int log_size;
+
+__attribute__((destructor)) static void write_log_file(void)
+{
+       int log_fd;
+       int ret;
+       char path[128];
+
+       snprintf(path, sizeof(path), "%s/%s", dirname(recovery_image_path), LOG_FILE_BASENAME);
+       log_fd = creat(path, 0644);
+       if (log_fd == -1) {
+               _E("Failed to open log file (%d)", errno);
+               return;
+       }
+
+       ret = write(log_fd, log_buf, log_size);
+       if (ret < log_size)
+               _E("Failed to write log (log size=%d, ret=%d)", log_size, ret);
+
+       close(log_fd);
+}
+#endif
+
+static int find_recovery_image(void)
+{
+       int ret = SUCCEED;
+       _CLEANUP_DIR_ DIR *dir = NULL;
+       struct dirent *dirent = NULL;
+       char path[PATH_MAX - FIELD_LENGTH];
+       int num_recovery_img = 0;
+
+       // Find USB mount path
+       _D("USB mountpoint root : %s", USB_MOUNTPOINT_ROOT);
+       dir = opendir(USB_MOUNTPOINT_ROOT);
+       ASSERT_RETV(dir, errno, "Failed to open USB mountpoint root (%d)", errno);
+
+       while ((dirent = readdir(dir))) {
+               if (dirent->d_type == DT_DIR &&
+                       !strncmp(dirent->d_name, USB_MOUNTPOINT_PREFIX, sizeof(USB_MOUNTPOINT_PREFIX) - 1)) {
+                       _I("Find mountpoint : %s/%s", USB_MOUNTPOINT_ROOT, dirent->d_name);
+
+                       snprintf(path, sizeof(path), "%s/%s/%s",
+                                       USB_MOUNTPOINT_ROOT, dirent->d_name, RECOVERY_IMAGE_BASENAME);
+                       ret = access(path, F_OK);
+                       switch (ret) {
+                       case 0:
+                               _I("Find recovery image : %s", path);
+
+                               /**
+                                * Stop process with many recovery images
+                                * to prevent working with unintended image
+                                */
+                               ASSERT_RETV(num_recovery_img++ == 0, EMFILE,
+                                               "There are many recovery images. Please put exactly one image");
+
+                               snprintf(recovery_image_path, sizeof(recovery_image_path), "%s", path);
+                               break;
+                       case ENOENT:
+                               break;
+                       default:
+                               _E("access for %s failed (%d)", path, errno);
+                               return errno;
+                       }
+               }
+       }
+       ASSERT_RETV(num_recovery_img == 1, ENFILE, "There is no recovery image");
+
+       return SUCCEED;
+}
+
+static int mount_recovery_image(void)
+{
+       int ret;
+       _CLEANUP_FD_ int loopctl_fd = -1;
+       _CLEANUP_FD_ int loop_fd = -1;
+       _CLEANUP_FD_ int img_fd = -1;
+       int loop_idx;
+       char loop_device_path[32];
+
+       // Get loop device path
+       loopctl_fd = open("/dev/loop-control", O_RDWR);
+       ASSERT_RETV(loopctl_fd >= 0, errno, "Failed to open loop control device (%d)", errno);
+
+       loop_idx = ioctl(loopctl_fd, LOOP_CTL_GET_FREE);
+       ASSERT_RETV(loop_idx >= 0, errno, "Failed to get loop device index (%d)", errno);
+
+       snprintf(loop_device_path, sizeof(loop_device_path), "/dev/loop%d", loop_idx);
+       _D("Loop device path : %s", loop_device_path);
+
+       // Make loop device
+       loop_fd = open(loop_device_path, O_RDONLY);
+       ASSERT_RETV(loop_fd >= 0, errno, "Failed to open loop device (%d)", errno);
+
+       img_fd = open(recovery_image_path, O_RDONLY);
+       ASSERT_RETV(img_fd >= 0, errno, "Failed to open recovery image (%d)", errno);
+
+       ret = ioctl(loop_fd, LOOP_SET_FD, img_fd);
+       ASSERT_RETV(ret == 0, errno, "Failed to set loop device (%d)", errno);
+
+       // Mount recovery image
+       _D("Try to mount : %s -> %s", loop_device_path, RECOVERY_IMAGE_MOUNTPOINT);
+       ret = mkdir(RECOVERY_IMAGE_MOUNTPOINT, 0755);
+       ASSERT_RETV(ret == 0, errno, "Failed to make mountpoint directory (%d)", errno);
+
+       ret = mount(loop_device_path, RECOVERY_IMAGE_MOUNTPOINT,
+                       "squashfs", MS_RDONLY, NULL);
+       ASSERT_RETV(ret == 0, errno, "Failed to mount recovery image (%d)", errno);
+
+       return SUCCEED;
+}
+
+static int read_config_file(struct image **image_list)
+{
+       int ret;
+       _CLEANUP_FP_ FILE *fp = NULL;
+       struct image ibuf = { 0, };
+       struct image *image = NULL;
+       struct image *image_last = NULL;
+
+       // Read config file
+       _D("Config file path : %s", RECOVERY_CONFIG_FILE_PATH);
+       fp = fopen(RECOVERY_CONFIG_FILE_PATH, "r");
+       ASSERT_RETV(fp, errno, "Failed to open config file (%d)", errno);
+
+       // Store image configuration
+       _I("=== Image list start ===");
+       while ((ret = fscanf(fp, "%s %s %s\n", ibuf.label, ibuf.basename, ibuf.devpath) != EOF)) {
+               _I("Label(%s), Basename(%s), Devpath(%s)", ibuf.label, ibuf.basename, ibuf.devpath);
+
+               image = calloc(1, sizeof(struct image));
+               ASSERT_RETV(image, ENOMEM, "Failed to allocate memory");
+
+               memcpy(image, &ibuf, sizeof(struct image));
+
+               if (image_last) {
+                       image_last->next = image;
+                       image_last = image;
+               } else {
+                       *image_list = image;
+                       image_last = image;
+               }
+       }
+       _I("=== Image list end ===");
+
+       ret = SUCCEED;
+
+       return ret;
+}
+
+static int do_recovery(struct image *image_list)
+{
+       int fd_src;
+       int fd_dst;
+       char path[PATH_MAX];
+       char buf[4096];
+       size_t data_size;
+
+       _I("=== Image writing start ===");
+       while (image_list) {
+               snprintf(path, sizeof(path), "%s/%s", RECOVERY_IMAGE_MOUNTPOINT, image_list->basename);
+               _I("Label(%s), Imagepath(%s), Devpath(%s)",
+                               image_list->label, path, image_list->devpath);
+
+               fd_src = open(path, O_RDONLY);
+               ASSERT_RETV(fd_src != -1, errno, "Failed to open image (%d)", errno);
+
+               fd_dst = open(image_list->devpath, O_WRONLY);
+               ASSERT_RETV(fd_dst, errno, "Failed to open device (%d)", errno);
+
+               while ((data_size = read(fd_src, buf, sizeof(buf))) > 0) {
+                       ASSERT_RETV(write(fd_dst, buf, data_size) == data_size, errno,
+                                       "Failed to write data (%d)", errno);
+               }
+
+               close(fd_dst);
+               close(fd_src);
+               image_list = image_list->next;
+       }
+       _I("=== Image writing end ===");
+
+       return SUCCEED;
+}
+
+int main(void)
+{
+       int ret;
+       _CLEANUP_IMAGE_ struct image *image_list = NULL;
+
+       ret = find_recovery_image();
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to find recovery image(%d)", ret);
+
+       ret = mount_recovery_image();
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to mount recovery image (%d)", ret);
+
+       ret = read_config_file(&image_list);
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to read config file (%d)", ret);
+
+       ret = do_recovery(image_list);
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to do recovery (%d)", ret);
+
+       _I("Succeed in image recovery");
+       ret = SUCCEED;
+
+       return ret;
+}
diff --git a/src/system-recovery.h.in b/src/system-recovery.h.in
new file mode 100644 (file)
index 0000000..2e88a6f
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef __SYSTEM_RECOVERY_H__
+#define __SYSTEM_RECOVERY_H__
+
+#define SUCCEED 0
+
+#define ASSERT_RETV(cond, retv, msg, marg...)  \
+do {   \
+       if (!(cond)) {  \
+               _E(msg, ##marg);        \
+               return retv;    \
+       }       \
+} while (0)
+
+
+#define USB_MOUNTPOINT_ROOT "@USB_MOUNTPOINT_ROOT@"
+#define USB_MOUNTPOINT_PREFIX "@USB_MOUNTPOINT_PREFIX@"
+
+#define RECOVERY_IMAGE_BASENAME "tizen-recovery.img"
+#define RECOVERY_IMAGE_MOUNTPOINT "/tmp/recovery_image"
+#define RECOVERY_CONFIG_FILE_PATH RECOVERY_IMAGE_MOUNTPOINT "/recovery.cfg"
+
+#define LOG_FILE_BASENAME "last_recovery.log"
+
+#define FIELD_LENGTH 32
+
+struct image {
+       char label[FIELD_LENGTH];
+       char basename[FIELD_LENGTH];
+       char devpath[FIELD_LENGTH];
+       struct image *next;
+};
+
+/********** Log **********/
+
+//#define LOG_STDOUT
+#define LOG_FILE
+
+//#define LOG_VERBOSE
+
+
+#if defined(LOG_STDOUT)
+#define _L(lvl, fmt, arg...) printf("SR/" lvl fmt "\n", ##arg)
+#elif defined(LOG_FILE)
+extern char log_buf[8192];
+extern int log_size;
+#define _L(lvl, fmt, arg...)   \
+do {   \
+       printf("SR/" lvl fmt "\n", ##arg);      \
+       log_size += snprintf(log_buf + log_size, sizeof(log_buf) - log_size, "SR/" lvl fmt "\n", ##arg);        \
+} while (0)
+#else
+#define _L(lvl, fmt, arg...)
+#endif
+
+#ifdef LOG_VERBOSE
+#define _D(fmt, arg...) _L("DEBUG ", fmt, ##arg)
+#else
+#define _D(fmt, arg...)
+#endif
+#define _I(fmt, arg...) _L("INFO  ", fmt, ##arg)
+#define _W(fmt, arg...) _L("WARN  ", fmt, ##arg)
+#define _E(fmt, arg...) _L("ERROR ", fmt, ##arg)
+
+/********** Cleanup Functions **********/
+
+static inline void close_fd(int *fd)
+{
+       if (fd && *fd >= 0)
+               close(*fd);
+}
+
+static inline void close_fp(FILE **fp)
+{
+       if (fp && *fp)
+               fclose(*fp);
+}
+
+static inline void close_dir(DIR **dir)
+{
+       if (dir && *dir)
+               closedir(*dir);
+}
+
+static inline void free_image(struct image **image)
+{
+       struct image *image_cur = *image;
+       struct image *image_next;
+
+       while (image_cur) {
+               image_next = image_cur->next;
+               free(image_cur);
+               image_cur = image_next;
+       }
+}
+
+#define _CLEANUP_(func) __attribute__((__cleanup__(func)))
+#define _CLEANUP_FD_ _CLEANUP_(close_fd)
+#define _CLEANUP_FP_ _CLEANUP_(close_fp)
+#define _CLEANUP_DIR_ _CLEANUP_(close_dir)
+#define _CLEANUP_IMAGE_ _CLEANUP_(free_image)
+
+#endif /* __SYSTEM_RECOVERY_H__ */