diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | packaging/initrd-recovery.spec | 16 | ||||
-rw-r--r-- | src/bootmode-recovery/50-recovery.list.in | 41 | ||||
-rw-r--r-- | src/bootmode-recovery/CMakeLists.txt | 15 | ||||
-rw-r--r-- | src/bootmode-recovery/recovery-init.in | 194 | ||||
-rw-r--r-- | src/bootmode-recovery/recovery-reboot.service.in | 11 | ||||
-rw-r--r-- | src/bootmode-recovery/recovery.service | 11 | ||||
-rw-r--r-- | src/bootmode-recovery/recovery.target | 7 | ||||
-rw-r--r-- | src/bootmode-recovery/system-recovery.c | 400 | ||||
-rw-r--r-- | src/bootmode-recovery/system-recovery.h.in | 112 |
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__ */ |