5 #include <linux/loop.h>
10 #include <sys/ioctl.h>
11 #include <sys/mount.h>
13 #include <sys/types.h>
17 #include "system-recovery.h"
20 static int loop_fd = -1;
22 static int recovery_image_mounted;
23 static char recovery_image_path[PATH_MAX - FIELD_LENGTH];
25 static unsigned long long total_image_size;
30 * Log is written to the buffer and written to the USB
31 * before termination because of these issues;
33 * - We can't write to file before finding USB mountpoint
34 * - Mounting same place twice is not allowed : log and recovery image
40 static int write_log_file(void)
42 _CLEANUP_FD_ int log_fd = -1;
46 ASSERT_RETV(recovery_image_path[0], EINVAL, "Recovery image path is empty");
48 snprintf(path, sizeof(path), "%s/%s", dirname(recovery_image_path), LOG_FILE_BASENAME);
49 log_fd = creat(path, 0644);
50 ASSERT_RETV(log_fd != -1, errno, "Failed to open log file (%d)", errno);
52 ret = write(log_fd, log_buf, log_size);
53 ASSERT_RETV(ret == log_size, EIO, "Failed to write log (log size=%d, ret=%d)", log_size, ret);
59 static int release_recovery_image(void)
63 if (recovery_image_mounted) {
64 ret = umount(RECOVERY_IMAGE_MOUNTPOINT);
65 ASSERT_RETV(ret == 0, errno, "Failed to unmount recovery image (%d)", errno);
69 ret = ioctl(loop_fd, LOOP_CLR_FD, 0);
70 ASSERT_RETV(ret == 0, errno, "Failed to clear loop device (%d)", errno);
76 __attribute__((destructor)) static void __fini__(void)
81 ret = write_log_file();
83 _W("Failed to write log file (%d)", ret);
86 ret = release_recovery_image();
88 _W("Failed to release recovery image (%d)", ret);
92 static int find_recovery_image(void)
95 _CLEANUP_DIR_ DIR *dir = NULL;
96 struct dirent *dirent = NULL;
97 char path[PATH_MAX - FIELD_LENGTH];
98 int num_recovery_img = 0;
100 // Find USB mount path
101 _D("USB mountpoint root : %s", USB_MOUNTPOINT_ROOT);
102 dir = opendir(USB_MOUNTPOINT_ROOT);
103 ASSERT_RETV(dir, errno, "Failed to open USB mountpoint root (%d)", errno);
105 while ((dirent = readdir(dir))) {
106 if (dirent->d_type == DT_DIR &&
107 !strncmp(dirent->d_name, USB_MOUNTPOINT_PREFIX, sizeof(USB_MOUNTPOINT_PREFIX) - 1)) {
108 _I("Find mountpoint : %s/%s", USB_MOUNTPOINT_ROOT, dirent->d_name);
110 snprintf(path, sizeof(path), "%s/%s/%s",
111 USB_MOUNTPOINT_ROOT, dirent->d_name, RECOVERY_IMAGE_BASENAME);
112 ret = access(path, F_OK);
114 _I("Find recovery image : %s", path);
117 * Stop process with many recovery images
118 * to prevent working with unintended image
121 ASSERT_RETV(num_recovery_img == 1, EMFILE,
122 "There are many recovery images. Please put exactly one image");
124 snprintf(recovery_image_path, sizeof(recovery_image_path), "%s", path);
125 } else if (errno != ENOENT) {
126 _E("access for %s failed (%d)", path, errno);
131 ASSERT_RETV(num_recovery_img == 1, ENFILE, "There is no recovery image");
136 static int try_launch(const char *argv, ...)
138 const char *path = argv;
143 const char *arr[256];
147 _E("Invalid parameter");
151 ret = access(path, F_OK);
153 _I("Find %s... Try to launch it", path);
154 else if (errno == ENOENT) {
155 _W("%s not found... Skip it", path);
158 _E("access for %s failed (%d)", path, errno);
168 // Child : execute program
170 while (argv && num_arg < 255) {
171 arr[num_arg++] = argv;
172 argv = va_arg(ap, const char *);
177 ret = execvp(path, (char * const *)arr);
178 ASSERT_RETV(ret != -1, errno, "execvp for %s failed (%d)", path, errno);
182 // Parent : receive child's result
184 ASSERT_RETV(ret != -1, errno, "wait failed (%d)", errno);
186 ASSERT_RETV(WIFEXITED(status), EIO,
187 "%s doesn't terminated normally", path);
188 ASSERT_RETV(WEXITSTATUS(status) == SUCCEED, WEXITSTATUS(status),
189 "%s failed (%d)", path, WEXITSTATUS(status));
196 static int verify_recovery_image(void)
198 return try_launch(IMAGE_VERIFIER_PATH, "-i", recovery_image_path, NULL);
201 static int mount_recovery_image(void)
204 _CLEANUP_FD_ int loopctl_fd = -1;
205 _CLEANUP_FD_ int img_fd = -1;
207 char loop_device_path[32];
209 // Get loop device path
210 loopctl_fd = open("/dev/loop-control", O_RDWR);
211 ASSERT_RETV(loopctl_fd >= 0, errno, "Failed to open loop control device (%d)", errno);
213 loop_idx = ioctl(loopctl_fd, LOOP_CTL_GET_FREE);
214 ASSERT_RETV(loop_idx >= 0, errno, "Failed to get loop device index (%d)", errno);
216 snprintf(loop_device_path, sizeof(loop_device_path), "/dev/loop%d", loop_idx);
217 _D("Loop device path : %s", loop_device_path);
220 loop_fd = open(loop_device_path, O_RDONLY);
221 ASSERT_RETV(loop_fd >= 0, errno, "Failed to open loop device (%d)", errno);
223 img_fd = open(recovery_image_path, O_RDONLY);
224 ASSERT_RETV(img_fd >= 0, errno, "Failed to open recovery image (%d)", errno);
226 ret = ioctl(loop_fd, LOOP_SET_FD, img_fd);
227 ASSERT_RETV(ret == 0, errno, "Failed to set loop device (%d)", errno);
229 // Mount recovery image
230 _D("Try to mount : %s -> %s", loop_device_path, RECOVERY_IMAGE_MOUNTPOINT);
231 ret = mkdir(RECOVERY_IMAGE_MOUNTPOINT, 0755);
232 ASSERT_RETV(ret == 0, errno, "Failed to make mountpoint directory (%d)", errno);
234 ret = mount(loop_device_path, RECOVERY_IMAGE_MOUNTPOINT,
235 "squashfs", MS_RDONLY, NULL);
236 ASSERT_RETV(ret == 0, errno, "Failed to mount recovery image (%d)", errno);
238 recovery_image_mounted = 1;
242 static int run_setup_script(void)
244 return try_launch(SETUP_SCRIPT_PATH, NULL);
247 static int read_config_file(struct image **image_list)
250 _CLEANUP_FP_ FILE *fp = NULL;
251 struct image ibuf = { 0, };
252 struct image *image = NULL;
253 struct image *image_last = NULL;
258 _D("Config file path : %s", RECOVERY_CONFIG_FILE_PATH);
259 fp = fopen(RECOVERY_CONFIG_FILE_PATH, "r");
260 ASSERT_RETV(fp, errno, "Failed to open config file (%d)", errno);
262 // Store image configuration
263 _I("=== Image list start ===");
264 while ((ret = fscanf(fp, "%" IMAGE_FIELD_LENGTH_STR "s"
265 "%" IMAGE_FIELD_LENGTH_STR "s"
266 "%" IMAGE_FIELD_LENGTH_STR "s\n",
269 ibuf.devpath) != EOF)) {
270 _I("Label(%s), Basename(%s), Devpath(%s)", ibuf.label, ibuf.basename, ibuf.devpath);
272 snprintf(path, sizeof(path), "%s/%s", RECOVERY_IMAGE_MOUNTPOINT, ibuf.basename);
273 ret = stat(path, &statbuf);
274 ASSERT_RETV(ret == 0, errno, "Failed to get file status (%d)", errno);
276 _I("Image size : %llu", (unsigned long long)statbuf.st_size);
277 total_image_size += statbuf.st_size;
279 image = calloc(1, sizeof(struct image));
280 ASSERT_RETV(image, ENOMEM, "Failed to allocate memory");
282 memcpy(image, &ibuf, sizeof(struct image));
285 image_last->next = image;
292 _I("=== Image list end ===");
293 _I("Total image size : %llu bytes", total_image_size);
300 static int update_progress(ssize_t written_size)
302 static int last_percent = 0;
303 static unsigned long long total_written_size = 0;
304 double current_percent;
307 total_written_size += written_size;
308 current_percent = (double)total_written_size * 100.0 / total_image_size;
310 //_D("Written size : %llu(+%u)/%llu bytes", total_written_size, written_size, total_image_size);
312 // Write current progress when percentage is increased
313 if ((int)current_percent > last_percent) {
314 _I("Progress : %d%%", (int)current_percent);
316 fd = creat(PROGRESS_FILE_PATH, 0644);
317 ASSERT_RETV(fd >= 0, errno, "Failed to create progress file");
319 last_percent = (int)current_percent;
320 ASSERT_RETV(dprintf(fd, "%d\n", last_percent) > 0, EIO, "Failed to write current progress");
321 ASSERT_RETV(close(fd) == 0, errno, "Failed to close progress file (%d)", errno);
327 static int do_recovery(struct image *image_list)
329 _CLEANUP_FD_ int fd_src = -1;
330 _CLEANUP_FD_ int fd_dst = -1;
335 _I("=== Image writing start ===");
337 snprintf(path, sizeof(path), "%s/%s", RECOVERY_IMAGE_MOUNTPOINT, image_list->basename);
338 _I("Label(%s), Imagepath(%s), Devpath(%s)",
339 image_list->label, path, image_list->devpath);
341 fd_src = open(path, O_RDONLY);
342 ASSERT_RETV(fd_src != -1, errno, "Failed to open image (%d)", errno);
344 fd_dst = open(image_list->devpath, O_WRONLY);
345 ASSERT_RETV(fd_dst != -1, errno, "Failed to open device (%d)", errno);
347 while ((data_size = read(fd_src, buf, sizeof(buf))) > 0) {
348 update_progress(data_size);
349 ASSERT_RETV(write(fd_dst, buf, data_size) == data_size, errno,
350 "Failed to write data (%d)", errno);
359 image_list = image_list->next;
361 _I("=== Image writing end ===");
369 _CLEANUP_IMAGE_ struct image *image_list = NULL;
371 ret = find_recovery_image();
372 ASSERT_RETV(ret == SUCCEED, ret, "Failed to find recovery image(%d)", ret);
374 ret = verify_recovery_image();
375 ASSERT_RETV(ret == SUCCEED, ret, "Failed to verify recovery image (%d)", ret);
377 ret = mount_recovery_image();
378 ASSERT_RETV(ret == SUCCEED, ret, "Failed to mount recovery image (%d)", ret);
380 ret = run_setup_script();
381 ASSERT_RETV(ret == SUCCEED, ret, "Failed to run setup script (%d)", ret);
383 ret = read_config_file(&image_list);
384 ASSERT_RETV(ret == SUCCEED, ret, "Failed to read config file (%d)", ret);
386 ret = do_recovery(image_list);
387 ASSERT_RETV(ret == SUCCEED, ret, "Failed to do recovery (%d)", ret);
389 _I("Succeed in image recovery");