--- /dev/null
+#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;
+}
--- /dev/null
+#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__ */