summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--packaging/initrd-recovery.spec16
-rw-r--r--src/bootmode-recovery/50-recovery.list.in41
-rw-r--r--src/bootmode-recovery/CMakeLists.txt15
-rw-r--r--src/bootmode-recovery/recovery-init.in194
-rw-r--r--src/bootmode-recovery/recovery-reboot.service.in11
-rw-r--r--src/bootmode-recovery/recovery.service11
-rw-r--r--src/bootmode-recovery/recovery.target7
-rw-r--r--src/bootmode-recovery/system-recovery.c400
-rw-r--r--src/bootmode-recovery/system-recovery.h.in112
10 files changed, 807 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7205e54..92e7567 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/packaging/initrd-recovery.spec b/packaging/initrd-recovery.spec
index 13fd2d6..14d43d8 100644
--- a/packaging/initrd-recovery.spec
+++ b/packaging/initrd-recovery.spec
@@ -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
index 0000000..ea4462c
--- /dev/null
+++ b/src/bootmode-recovery/50-recovery.list.in
@@ -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
index 0000000..5efcff7
--- /dev/null
+++ b/src/bootmode-recovery/CMakeLists.txt
@@ -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
index 0000000..281d2d6
--- /dev/null
+++ b/src/bootmode-recovery/recovery-init.in
@@ -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
index 0000000..5e1476d
--- /dev/null
+++ b/src/bootmode-recovery/recovery-reboot.service.in
@@ -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
index 0000000..5f77381
--- /dev/null
+++ b/src/bootmode-recovery/recovery.service
@@ -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
index 0000000..8fe10d0
--- /dev/null
+++ b/src/bootmode-recovery/recovery.target
@@ -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
index 0000000..f204b3e
--- /dev/null
+++ b/src/bootmode-recovery/system-recovery.c
@@ -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
index 0000000..ac9ef6e
--- /dev/null
+++ b/src/bootmode-recovery/system-recovery.h.in
@@ -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__ */