Import system-recovery 99/280299/2 accepted/tizen_7.0_unified_hotfix tizen_7.0_hotfix accepted/tizen/7.0/unified/hotfix/20221116.105729 accepted/tizen/unified/20220902.125513 submit/tizen/20220901.220252 tizen_7.0_m2_release
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Thu, 25 Aug 2022 14:01:12 +0000 (16:01 +0200)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Fri, 26 Aug 2022 09:49:16 +0000 (11:49 +0200)
This commit imports system-recovery from commit ea876f763b54 ("Mount hal
partition during recovery").

Following changes were made with respect to original repository:

 - use initrd-recovery variables in packaging/, cmake & files

 - include recovery-init as VERBATIM (not MVWITHLIBS) in 50-recovery.list

Change-Id: Ifda2a021b39b4421dd81baf690d539fbb6347504

CMakeLists.txt
packaging/initrd-recovery.spec
src/bootmode-recovery/50-recovery.list.in [new file with mode: 0644]
src/bootmode-recovery/CMakeLists.txt [new file with mode: 0644]
src/bootmode-recovery/recovery-init.in [new file with mode: 0644]
src/bootmode-recovery/recovery-reboot.service.in [new file with mode: 0644]
src/bootmode-recovery/recovery.service [new file with mode: 0644]
src/bootmode-recovery/recovery.target [new file with mode: 0644]
src/bootmode-recovery/system-recovery.c [new file with mode: 0644]
src/bootmode-recovery/system-recovery.h.in [new file with mode: 0644]

index 7205e54..92e7567 100644 (file)
@@ -8,4 +8,5 @@ ADD_SUBDIRECTORY(src/initrd-recovery)
 ADD_SUBDIRECTORY(src/minireboot)
 ADD_SUBDIRECTORY(src/bow-restore)
 ADD_SUBDIRECTORY(src/bootmode-fota)
+ADD_SUBDIRECTORY(src/bootmode-recovery)
 ADD_SUBDIRECTORY(units)
index 13fd2d6..14d43d8 100644 (file)
@@ -1,6 +1,6 @@
 Name:           initrd-recovery
 Summary:        Tools for system recovery
-Version:        7.5.0
+Version:        7.5.1
 Release:        0
 Group:          System/Utilities
 License:        Apache-2.0
@@ -73,6 +73,11 @@ ln -s ../mnt-inform.mount %{buildroot}%{_unitdir}/local-fs.target.wants
 
 mkdir -p %{buildroot}/opt/data/update
 
+# recovery
+mkdir -p %{buildroot}%{_unitdir}/recovery.target.wants
+ln -s ../recovery-reboot.service %{buildroot}%{_unitdir}/recovery.target.wants
+ln -s ../recovery.service %{buildroot}%{_unitdir}/recovery.target.wants
+
 %post
 chsmack -e "System::Privileged" %{buildroot}%{initrd_recovery_libexec_dir}/progress_restart.sh
 
@@ -103,6 +108,15 @@ rm -rf %{_libdir}/initrd-recovery
 %{_sbindir}/bow-restore
 %attr(775,system,system) /opt/data/update
 
+%{initrd_recovery_libexec_dir}/recovery-init
+%{initrd_recovery_libexec_dir}/system-recovery
+%{initrd_recovery_install_dropin_dir}/50-recovery.list
+%{_unitdir}/recovery.target
+%{_unitdir}/recovery-reboot.service
+%{_unitdir}/recovery.service
+%{_unitdir}/recovery.target.wants/recovery-reboot.service
+%{_unitdir}/recovery.target.wants/recovery.service
+
 %files -n reboot-param-helper
 %{_unitdir}/mnt-inform.mount
 %{_unitdir}/local-fs.target.wants/mnt-inform.mount
diff --git a/src/bootmode-recovery/50-recovery.list.in b/src/bootmode-recovery/50-recovery.list.in
new file mode 100644 (file)
index 0000000..ea4462c
--- /dev/null
@@ -0,0 +1,41 @@
+# ---- Target contents ----------------------------------------------------- #
+DIRECTORIES="
+/dev
+/etc
+/proc
+/smack
+/sys
+/system
+/tmp
+/usr/bin
+/usr/lib
+/usr/sbin
+"
+
+MVWITHLIBS="
+@INITRD_RECOVERY_LIBEXEC_DIR@/system-recovery
+@INITRD_RECOVERY_LIBEXEC_DIR@/minireboot
+"
+
+VERBATIMS="
+@INITRD_RECOVERY_LIBEXEC_DIR@/recovery-init
+"
+
+WITHLIBS="
+/usr/bin/cut
+/usr/bin/ls
+/usr/bin/mkdir
+/usr/bin/mount
+/usr/bin/rmdir
+/usr/bin/sh
+/usr/bin/sync
+/usr/bin/tr
+/usr/bin/umount
+/usr/sbin/agetty
+/usr/sbin/blkid
+"
+
+# LinkFileName:Target
+SYMLINKS="
+/sbin/recovery-init:@INITRD_RECOVERY_LIBEXEC_DIR@/recovery-init
+"
diff --git a/src/bootmode-recovery/CMakeLists.txt b/src/bootmode-recovery/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5efcff7
--- /dev/null
@@ -0,0 +1,15 @@
+set(USB_MOUNTPOINT_ROOT /tmp)
+set(USB_MOUNTPOINT_PREFIX USBDrive)
+
+configure_file(recovery-reboot.service.in recovery-reboot.service @ONLY)
+configure_file(recovery-init.in recovery-init @ONLY)
+configure_file(50-recovery.list.in 50-recovery.list @ONLY)
+
+install(FILES recovery-reboot.service recovery.target recovery.service DESTINATION ${UNIT_DIR})
+install(FILES recovery-init DESTINATION ${INITRD_RECOVERY_LIBEXEC_DIR})
+install(FILES 50-recovery.list DESTINATION ${INITRD_RECOVERY_INSTALL_DROPIN_DIR})
+
+configure_file(system-recovery.h.in system-recovery.h @ONLY)
+add_executable(system-recovery system-recovery.c)
+target_link_libraries(system-recovery ${system-recovery_LDFLAGS} "-ldl")
+install(TARGETS system-recovery DESTINATION ${INITRD_RECOVERY_LIBEXEC_DIR})
diff --git a/src/bootmode-recovery/recovery-init.in b/src/bootmode-recovery/recovery-init.in
new file mode 100644 (file)
index 0000000..281d2d6
--- /dev/null
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+SYSTEM_RECOVERY="@INITRD_RECOVERY_LIBEXEC_DIR@/system-recovery"
+SYSTEM_RECOVERY_GUI="/usr/bin/system-recovery_gui"
+
+MOUNT="/usr/bin/mount"
+BLKID="/usr/sbin/blkid"
+REBOOT="@INITRD_RECOVERY_LIBEXEC_DIR@/minireboot"
+SYNC="/usr/bin/sync"
+UMOUNT="/usr/bin/umount"
+CUT="/usr/bin/cut"
+LS="/usr/bin/ls"
+MKDIR="/usr/bin/mkdir"
+RMDIR="/usr/bin/rmdir"
+TR="/usr/bin/tr"
+
+FAKE_ROOT="/system-ro"
+HAL_MNT=hal
+#------------------------------------------------
+#       mount_usb_partitions
+#------------------------------------------------
+USB_MOUNTPOINT_PREFIX=@USB_MOUNTPOINT_ROOT@/@USB_MOUNTPOINT_PREFIX@
+mount_usb_partitions() {
+    echo "mount USB partitions"
+    DEVICE_TYPE_LIST=(
+        ext4
+        vfat
+    )
+
+    LAST_MOUNTED_DEVICE=""
+    for DEVICE_TYPE in ${DEVICE_TYPE_LIST[@]}
+    do
+        DEVICES=""
+        for ((i=0; i<10; i++))
+        do
+            DEVICES=$("$BLKID" /dev/sd* -t TYPE="${DEVICE_TYPE}" -o device)
+            if [ ! -z "${DEVICES}" ]
+            then
+                for DEVICE in ${DEVICES}
+                do
+                    USB_MOUNTPOINT_INDEX=$(echo ${DEVICE} | "$CUT" -b 8- | "$TR" '[:lower:]' '[:upper:]')
+                    USB_MOUNTPOINT=${USB_MOUNTPOINT_PREFIX}${USB_MOUNTPOINT_INDEX}
+                    "$MKDIR" -p ${USB_MOUNTPOINT}
+                    "$MOUNT" -t ${DEVICE_TYPE} ${DEVICE} ${USB_MOUNTPOINT}
+
+                    LAST_MOUNTED_DEVICE=${DEVICE}
+                done
+                break
+            fi
+
+            sleep 1
+        done
+    done
+
+    if [ -z "${LAST_MOUNTED_DEVICE}" ]
+    then
+        echo "WARNING : USB not mounted"
+    fi
+}
+
+#------------------------------------------------
+#       umount_usb_partitions
+#------------------------------------------------
+umount_usb_partitions() {
+    echo "umount USB partitions"
+    USB_MOUNTPOINTS=$("$LS" -d ${USB_MOUNTPOINT_PREFIX}*)
+    for USB_MOUNTPOINT in ${USB_MOUNTPOINTS}
+    do
+        "$UMOUNT" ${USB_MOUNTPOINT}
+        "$RMDIR" ${USB_MOUNTPOINT}
+    done
+}
+
+#------------------------------------------------
+#       do_reboot
+#------------------------------------------------
+do_reboot() {
+    echo "Reboot"
+    "$SYNC"
+    "$REBOOT"
+    while [ 1 ]
+    do
+        sleep 1
+        echo "."
+    done
+}
+
+#------------------------------------------------
+#       get partition id
+#------------------------------------------------
+get_partition_id() {
+    P_SLOT=$([[ $(</proc/cmdline) =~ partition_ab=([ab]) ]]; echo ${BASH_REMATCH[1]})
+    P_SUFFIX=""
+
+    if [ "${P_SLOT}" != "" ]; then
+        P_SUFFIX="_${P_SLOT}"
+        echo "Using A/B slot: ${P_SLOT}"
+    fi
+
+    PART_ROOTFS=`/sbin/blkid -t PARTLABEL=rootfs${P_SUFFIX} -o device -l`
+    if [ x$PART_ROOTFS = "x" ]
+    then
+        PART_ROOTFS=`/sbin/blkid -L rootfs`
+    fi
+
+    PART_SYSTEM_DATA=`/sbin/blkid -t PARTLABEL=system-data -o device -l`
+    if [ x$PART_SYSTEM_DATA = "x" ]
+    then
+        PART_SYSTEM_DATA=`/sbin/blkid -L system-data`
+    fi
+
+    PART_RAMDISK=`/sbin/blkid -t PARTLABEL=ramdisk${P_SUFFIX} -o device -l`
+    if [ x$PART_RAMDISK = "x" ]
+    then
+        PART_RAMDISK=`/sbin/blkid -L ramdisk`
+    fi
+
+    PART_HAL=`/sbin/blkid -t PARTLABEL=hal${P_SUFFIX} -o device -l`
+    if [ x$PART_HAL = "x" ]
+    then
+        PART_HAL=`/sbin/blkid -L hal`
+    fi
+
+    PART_USER=$("$BLKID" --match-token PARTLABEL=user -o device -l || "$BLKID" --match-token LABEL=user -o device -l)
+}
+
+#------------------------------------------------
+#       mount_rootfs
+#------------------------------------------------
+mount_rootfs()
+{
+       /usr/bin/verityctl create rootfs "${PART_ROOTFS}" "${FAKE_ROOT}"
+        case $? in
+            0)
+                echo "verifyboot: disabled"
+                # do nothing
+                ;;
+            1)
+                echo "verityboot: enabled";
+                return
+                ;;
+            2)
+                echo "verifyboot: enabled but corrupted"
+               do_reboot
+                ;;
+            3)
+                echo "verifyboot: disabling"
+                ;;
+        esac
+       "$MOUNT" -o ro "${PART_ROOTFS}" "${FAKE_ROOT}"
+}
+
+#------------------------------------------------
+#       mount_partitions
+#------------------------------------------------
+mount_partitions() {
+    get_partition_id
+
+    mount_rootfs
+    if [ ! "z${PART_HAL}" = "z" ]; then
+        "$MOUNT" -o ro "${PART_HAL}" "${FAKE_ROOT}/${HAL_MNT}"
+    fi
+    "$MOUNT" -t proc none ${FAKE_ROOT}/proc
+    "$MOUNT" -t sysfs none ${FAKE_ROOT}/sys
+    "$MOUNT" -t devtmpfs devtmpfs ${FAKE_ROOT}/dev
+    "$MOUNT" -t devpts devpts ${FAKE_ROOT}/dev/pts
+    "$MOUNT" -t tmpfs tmpfs ${FAKE_ROOT}/tmp
+}
+
+#------------------------------------------------
+#       run_recovery_target
+#------------------------------------------------
+run_recovery_target() {
+    cd ${FAKE_ROOT}
+    exec /bin/chroot . /usr/lib/systemd/systemd --unit=recovery.target $@
+}
+
+#------------------------------------------------
+#       system-recovery initrc
+#------------------------------------------------
+echo "Starting system recovery."
+mount_usb_partitions
+
+# For debugging - It should be deleted on Release
+/sbin/agetty -l /bin/sh -n --keep-baud 115200,38400,9600 console linux &
+
+[ -x "${SYSTEM_RECOVERY_GUI}" ] && "${SYSTEM_RECOVERY_GUI}" &
+[ -x "${SYSTEM_RECOVERY}" ] && "${SYSTEM_RECOVERY}"
+
+umount_usb_partitions
+mount_partitions
+run_recovery_target
+do_reboot
diff --git a/src/bootmode-recovery/recovery-reboot.service.in b/src/bootmode-recovery/recovery-reboot.service.in
new file mode 100644 (file)
index 0000000..5e1476d
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Reboot after recovery
+DefaultDependencies=no
+Requires=recovery.service
+After=recovery.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/device_board_clear_boot_mode
+ExecStart=/usr/sbin/reboot -f
+SmackProcessLabel=System
diff --git a/src/bootmode-recovery/recovery.service b/src/bootmode-recovery/recovery.service
new file mode 100644 (file)
index 0000000..5f77381
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Recovery
+DefaultDependencies=no
+Requires=sysinit.target local-fs.target
+After=sysinit.target local-fs.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/true
+SmackProcessLabel=System
diff --git a/src/bootmode-recovery/recovery.target b/src/bootmode-recovery/recovery.target
new file mode 100644 (file)
index 0000000..8fe10d0
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=System recovery
+Requires=sysinit.target
+After=sysinit.target
+AllowIsolate=yes
+RefuseManualStart=yes
+
diff --git a/src/bootmode-recovery/system-recovery.c b/src/bootmode-recovery/system-recovery.c
new file mode 100644 (file)
index 0000000..f204b3e
--- /dev/null
@@ -0,0 +1,400 @@
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libgen.h>
+#include <linux/loop.h>
+#include <stdarg.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 <sys/wait.h>
+#include <unistd.h>
+
+#include "system-recovery.h"
+
+
+static int loop_fd = -1;
+
+static int loop_device_associated;
+static int recovery_image_mounted;
+static char recovery_image_path[PATH_MAX - FIELD_LENGTH];
+
+static unsigned long long total_image_size;
+
+
+#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;
+
+static int write_log_file(void)
+{
+       _CLEANUP_FD_ int log_fd = -1;
+       int ret;
+       char path[128];
+
+       ASSERT_RETV(recovery_image_path[0], EINVAL, "Recovery image path is empty");
+
+       snprintf(path, sizeof(path), "%s/%s", dirname(recovery_image_path), LOG_FILE_BASENAME);
+       log_fd = creat(path, 0644);
+       ASSERT_RETV(log_fd != -1, errno, "Failed to open log file (%d)", errno);
+
+       ret = write(log_fd, log_buf, log_size);
+       ASSERT_RETV(ret == log_size, EIO, "Failed to write log (log size=%d, ret=%d)", log_size, ret);
+
+       return SUCCEED;
+}
+#endif
+
+static int release_recovery_image(void)
+{
+       int ret;
+
+       if (recovery_image_mounted) {
+               ret = umount(RECOVERY_IMAGE_MOUNTPOINT);
+               ASSERT_RETV(ret == 0, errno, "Failed to unmount recovery image (%d)", errno);
+       }
+
+       if (loop_device_associated) {
+               ret = ioctl(loop_fd, LOOP_CLR_FD, 0);
+               ASSERT_RETV(ret == 0, errno, "Failed to disassociate loop device (%d)", errno);
+       }
+
+       if (loop_fd >= 0) {
+               ret = close(loop_fd);
+               ASSERT_RETV(ret == 0, errno, "Failed to close loop device (%d)", errno);
+       }
+
+       return SUCCEED;
+}
+
+__attribute__((destructor)) static void __fini__(void)
+{
+       int ret = SUCCEED;
+
+#ifdef LOG_FILE
+       ret = write_log_file();
+       if (ret != SUCCEED)
+               _W("Failed to write log file (%d)", ret);
+#endif
+
+       ret = release_recovery_image();
+       if (ret != SUCCEED)
+               _W("Failed to release recovery image (%d)", ret);
+}
+
+
+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);
+                       if (ret == 0) {
+                               _I("Find recovery image : %s", path);
+
+                               /**
+                                * Stop process with many recovery images
+                                * to prevent working with unintended image
+                                */
+                               num_recovery_img++;
+                               ASSERT_RETV(num_recovery_img == 1, EMFILE,
+                                               "There are many recovery images. Please put exactly one image");
+
+                               snprintf(recovery_image_path, sizeof(recovery_image_path), "%s", path);
+                       } else if (errno != ENOENT) {
+                               _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 try_launch(const char *argv, ...)
+{
+       const char *path = argv;
+       int ret;
+       pid_t pid;
+       int status;
+       va_list ap;
+       const char *arr[256];
+       int num_arg = 0;
+
+       if (!path) {
+               _E("Invalid parameter");
+               return EINVAL;
+       }
+
+       ret = access(path, F_OK);
+       if (ret == 0)
+               _I("Find %s... Try to launch it", path);
+       else if (errno == ENOENT) {
+               _W("%s not found... Skip it", path);
+               return SUCCEED;
+       } else {
+               _E("access for %s failed (%d)", path, errno);
+               return errno;
+       }
+
+       pid = fork();
+       switch (pid) {
+       case -1:
+               _E("fork failed");
+               return EIO;
+       case 0:
+               // Child : execute program
+               va_start(ap, argv);
+               while (argv && num_arg < 255) {
+                       arr[num_arg++] = argv;
+                       argv = va_arg(ap, const char *);
+               }
+               va_end(ap);
+               arr[num_arg] = NULL;
+
+               ret = execvp(path, (char * const *)arr);
+               ASSERT_RETV(ret != -1, errno, "execvp for %s failed (%d)", path, errno);
+               exit(ret);
+               break;
+       default:
+               // Parent : receive child's result
+               ret = wait(&status);
+               ASSERT_RETV(ret != -1, errno, "wait failed (%d)", errno);
+
+               ASSERT_RETV(WIFEXITED(status), EIO,
+                               "%s doesn't terminated normally", path);
+               ASSERT_RETV(WEXITSTATUS(status) == SUCCEED, WEXITSTATUS(status),
+                               "%s failed (%d)", path, WEXITSTATUS(status));
+               return SUCCEED;
+       }
+
+       return EFAULT;
+}
+
+static int verify_recovery_image(void)
+{
+       return try_launch(IMAGE_VERIFIER_PATH, "-i", recovery_image_path, NULL);
+}
+
+static int mount_recovery_image(void)
+{
+       int ret;
+       _CLEANUP_FD_ int loopctl_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);
+       loop_device_associated = 1;
+
+       // 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);
+
+       recovery_image_mounted = 1;
+       return SUCCEED;
+}
+
+static int run_setup_script(void)
+{
+       return try_launch(SETUP_SCRIPT_PATH, NULL);
+}
+
+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;
+       struct stat statbuf;
+       char path[PATH_MAX];
+
+       // 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, "%" IMAGE_FIELD_LENGTH_STR "s"
+                                "%" IMAGE_FIELD_LENGTH_STR "s"
+                                "%" IMAGE_FIELD_LENGTH_STR "s\n",
+                                ibuf.label,
+                                ibuf.basename,
+                                ibuf.devpath) != EOF)) {
+               _I("Label(%s), Basename(%s), Devpath(%s)", ibuf.label, ibuf.basename, ibuf.devpath);
+
+               snprintf(path, sizeof(path), "%s/%s", RECOVERY_IMAGE_MOUNTPOINT, ibuf.basename);
+               ret = stat(path, &statbuf);
+               ASSERT_RETV(ret == 0, errno, "Failed to get file status (%d)", errno);
+
+               _I("Image size : %llu", (unsigned long long)statbuf.st_size);
+               total_image_size += statbuf.st_size;
+
+               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 ===");
+       _I("Total image size : %llu bytes", total_image_size);
+
+       ret = SUCCEED;
+
+       return ret;
+}
+
+static int update_progress(ssize_t written_size)
+{
+       static int last_percent = 0;
+       static unsigned long long total_written_size = 0;
+       double current_percent;
+       int fd;
+
+       total_written_size += written_size;
+       current_percent = (double)total_written_size * 100.0 / total_image_size;
+
+       //_D("Written size : %llu(+%u)/%llu bytes", total_written_size, written_size, total_image_size);
+
+       // Write current progress when percentage is increased
+       if ((int)current_percent > last_percent) {
+               _I("Progress : %d%%", (int)current_percent);
+
+               fd = creat(PROGRESS_FILE_PATH, 0644);
+               ASSERT_RETV(fd >= 0, errno, "Failed to create progress file");
+
+               last_percent = (int)current_percent;
+               ASSERT_RETV(dprintf(fd, "%d\n", last_percent) > 0, EIO, "Failed to write current progress");
+               ASSERT_RETV(close(fd) == 0, errno, "Failed to close progress file (%d)", errno);
+       }
+
+       return SUCCEED;
+}
+
+static int do_recovery(struct image *image_list)
+{
+       _CLEANUP_FD_ int fd_src = -1;
+       _CLEANUP_FD_ int fd_dst = -1;
+       char path[PATH_MAX];
+       char buf[4096];
+       ssize_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 != -1, errno, "Failed to open device (%d)", errno);
+
+               while ((data_size = read(fd_src, buf, sizeof(buf))) > 0) {
+                       update_progress(data_size);
+                       ASSERT_RETV(write(fd_dst, buf, data_size) == data_size, errno,
+                                       "Failed to write data (%d)", errno);
+               }
+
+               close(fd_dst);
+               close(fd_src);
+
+               fd_dst = -1;
+               fd_src = -1;
+
+               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 = verify_recovery_image();
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to verify recovery image (%d)", ret);
+
+       ret = mount_recovery_image();
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to mount recovery image (%d)", ret);
+
+       ret = run_setup_script();
+       ASSERT_RETV(ret == SUCCEED, ret, "Failed to run setup script (%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/bootmode-recovery/system-recovery.h.in b/src/bootmode-recovery/system-recovery.h.in
new file mode 100644 (file)
index 0000000..ac9ef6e
--- /dev/null
@@ -0,0 +1,112 @@
+#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 IMAGE_VERIFIER_PATH "/usr/sbin/img-verifier"
+
+#define SETUP_SCRIPT_PATH RECOVERY_IMAGE_MOUNTPOINT "/setup.sh"
+
+#define LOG_FILE_BASENAME "last_recovery.log"
+
+#define PROGRESS_FILE_PATH "/tmp/recovery_progress"
+
+#define FIELD_LENGTH 32
+#define STRINGIFY(x) __STRINGIFY(x)
+#define __STRINGIFY(x) #x
+
+#define IMAGE_FIELD_LENGTH_STR STRINGIFY(FIELD_LENGTH)
+
+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__ */