No more try to write log file without recovery image path
[platform/core/system/system-recovery.git] / src / system-recovery.c
1 #include <dirent.h>
2 #include <fcntl.h>
3 #include <errno.h>
4 #include <libgen.h>
5 #include <linux/loop.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/ioctl.h>
11 #include <sys/mount.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16
17 #include "system-recovery.h"
18
19
20 static int loop_fd = -1;
21
22 static int recovery_image_mounted;
23 static char recovery_image_path[PATH_MAX - FIELD_LENGTH];
24
25 static unsigned long long total_image_size;
26
27
28 #ifdef LOG_FILE
29 /**
30  * Log is written to the buffer and written to the USB
31  * before termination because of these issues;
32  *
33  * - We can't write to file before finding USB mountpoint
34  * - Mounting same place twice is not allowed : log and recovery image
35  */
36
37 char log_buf[8192];
38 int log_size;
39
40 static int write_log_file(void)
41 {
42         _CLEANUP_FD_ int log_fd = -1;
43         int ret;
44         char path[128];
45
46         ASSERT_RETV(recovery_image_path[0], EINVAL, "Recovery image path is empty");
47
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);
51
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);
54
55         return SUCCEED;
56 }
57 #endif
58
59 static int release_recovery_image(void)
60 {
61         int ret;
62
63         if (recovery_image_mounted) {
64                 ret = umount(RECOVERY_IMAGE_MOUNTPOINT);
65                 ASSERT_RETV(ret == 0, errno, "Failed to unmount recovery image (%d)", errno);
66         }
67
68         if (loop_fd >= 0) {
69                 ret = ioctl(loop_fd, LOOP_CLR_FD, 0);
70                 ASSERT_RETV(ret == 0, errno, "Failed to clear loop device (%d)", errno);
71         }
72
73         return SUCCEED;
74 }
75
76 __attribute__((destructor)) static void __fini__(void)
77 {
78         int ret = SUCCEED;
79
80 #ifdef LOG_FILE
81         ret = write_log_file();
82         if (ret != SUCCEED)
83                 _W("Failed to write log file (%d)", ret);
84 #endif
85
86         ret = release_recovery_image();
87         if (ret != SUCCEED)
88                 _W("Failed to release recovery image (%d)", ret);
89 }
90
91
92 static int find_recovery_image(void)
93 {
94         int ret = SUCCEED;
95         _CLEANUP_DIR_ DIR *dir = NULL;
96         struct dirent *dirent = NULL;
97         char path[PATH_MAX - FIELD_LENGTH];
98         int num_recovery_img = 0;
99
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);
104
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);
109
110                         snprintf(path, sizeof(path), "%s/%s/%s",
111                                         USB_MOUNTPOINT_ROOT, dirent->d_name, RECOVERY_IMAGE_BASENAME);
112                         ret = access(path, F_OK);
113                         if (ret == 0) {
114                                 _I("Find recovery image : %s", path);
115
116                                 /**
117                                  * Stop process with many recovery images
118                                  * to prevent working with unintended image
119                                  */
120                                 num_recovery_img++;
121                                 ASSERT_RETV(num_recovery_img == 1, EMFILE,
122                                                 "There are many recovery images. Please put exactly one image");
123
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);
127                                 return errno;
128                         }
129                 }
130         }
131         ASSERT_RETV(num_recovery_img == 1, ENFILE, "There is no recovery image");
132
133         return SUCCEED;
134 }
135
136 static int try_launch(const char *argv, ...)
137 {
138         const char *path = argv;
139         int ret;
140         pid_t pid;
141         int status;
142         va_list ap;
143         const char *arr[256];
144         int num_arg = 0;
145
146         if (!path) {
147                 _E("Invalid parameter");
148                 return EINVAL;
149         }
150
151         ret = access(path, F_OK);
152         if (ret == 0)
153                 _I("Find %s... Try to launch it", path);
154         else if (errno == ENOENT) {
155                 _W("%s not found... Skip it", path);
156                 return SUCCEED;
157         } else {
158                 _E("access for %s failed (%d)", path, errno);
159                 return errno;
160         }
161
162         pid = fork();
163         switch (pid) {
164         case -1:
165                 _E("fork failed");
166                 return EIO;
167         case 0:
168                 // Child : execute program
169                 va_start(ap, argv);
170                 while (argv && num_arg < 255) {
171                         arr[num_arg++] = argv;
172                         argv = va_arg(ap, const char *);
173                 }
174                 va_end(ap);
175                 arr[num_arg] = NULL;
176
177                 ret = execvp(path, (char * const *)arr);
178                 ASSERT_RETV(ret != -1, errno, "execvp for %s failed (%d)", path, errno);
179                 exit(ret);
180                 break;
181         default:
182                 // Parent : receive child's result
183                 ret = wait(&status);
184                 ASSERT_RETV(ret != -1, errno, "wait failed (%d)", errno);
185
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));
190                 return SUCCEED;
191         }
192
193         return EFAULT;
194 }
195
196 static int verify_recovery_image(void)
197 {
198         return try_launch(IMAGE_VERIFIER_PATH, "-i", recovery_image_path, NULL);
199 }
200
201 static int mount_recovery_image(void)
202 {
203         int ret;
204         _CLEANUP_FD_ int loopctl_fd = -1;
205         _CLEANUP_FD_ int img_fd = -1;
206         int loop_idx;
207         char loop_device_path[32];
208
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);
212
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);
215
216         snprintf(loop_device_path, sizeof(loop_device_path), "/dev/loop%d", loop_idx);
217         _D("Loop device path : %s", loop_device_path);
218
219         // Make loop device
220         loop_fd = open(loop_device_path, O_RDONLY);
221         ASSERT_RETV(loop_fd >= 0, errno, "Failed to open loop device (%d)", errno);
222
223         img_fd = open(recovery_image_path, O_RDONLY);
224         ASSERT_RETV(img_fd >= 0, errno, "Failed to open recovery image (%d)", errno);
225
226         ret = ioctl(loop_fd, LOOP_SET_FD, img_fd);
227         ASSERT_RETV(ret == 0, errno, "Failed to set loop device (%d)", errno);
228
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);
233
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);
237
238         recovery_image_mounted = 1;
239         return SUCCEED;
240 }
241
242 static int run_setup_script(void)
243 {
244         return try_launch(SETUP_SCRIPT_PATH, NULL);
245 }
246
247 static int read_config_file(struct image **image_list)
248 {
249         int ret;
250         _CLEANUP_FP_ FILE *fp = NULL;
251         struct image ibuf = { 0, };
252         struct image *image = NULL;
253         struct image *image_last = NULL;
254         struct stat statbuf;
255         char path[PATH_MAX];
256
257         // Read config file
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);
261
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",
267                                  ibuf.label,
268                                  ibuf.basename,
269                                  ibuf.devpath) != EOF)) {
270                 _I("Label(%s), Basename(%s), Devpath(%s)", ibuf.label, ibuf.basename, ibuf.devpath);
271
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);
275
276                 _I("Image size : %llu", (unsigned long long)statbuf.st_size);
277                 total_image_size += statbuf.st_size;
278
279                 image = calloc(1, sizeof(struct image));
280                 ASSERT_RETV(image, ENOMEM, "Failed to allocate memory");
281
282                 memcpy(image, &ibuf, sizeof(struct image));
283
284                 if (image_last) {
285                         image_last->next = image;
286                         image_last = image;
287                 } else {
288                         *image_list = image;
289                         image_last = image;
290                 }
291         }
292         _I("=== Image list end ===");
293         _I("Total image size : %llu bytes", total_image_size);
294
295         ret = SUCCEED;
296
297         return ret;
298 }
299
300 static int update_progress(ssize_t written_size)
301 {
302         static int last_percent = 0;
303         static unsigned long long total_written_size = 0;
304         double current_percent;
305         int fd;
306
307         total_written_size += written_size;
308         current_percent = (double)total_written_size * 100.0 / total_image_size;
309
310         //_D("Written size : %llu(+%u)/%llu bytes", total_written_size, written_size, total_image_size);
311
312         // Write current progress when percentage is increased
313         if ((int)current_percent > last_percent) {
314                 _I("Progress : %d%%", (int)current_percent);
315
316                 fd = creat(PROGRESS_FILE_PATH, 0644);
317                 ASSERT_RETV(fd >= 0, errno, "Failed to create progress file");
318
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);
322         }
323
324         return SUCCEED;
325 }
326
327 static int do_recovery(struct image *image_list)
328 {
329         _CLEANUP_FD_ int fd_src = -1;
330         _CLEANUP_FD_ int fd_dst = -1;
331         char path[PATH_MAX];
332         char buf[4096];
333         ssize_t data_size;
334
335         _I("=== Image writing start ===");
336         while (image_list) {
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);
340
341                 fd_src = open(path, O_RDONLY);
342                 ASSERT_RETV(fd_src != -1, errno, "Failed to open image (%d)", errno);
343
344                 fd_dst = open(image_list->devpath, O_WRONLY);
345                 ASSERT_RETV(fd_dst != -1, errno, "Failed to open device (%d)", errno);
346
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);
351                 }
352
353                 close(fd_dst);
354                 close(fd_src);
355
356                 fd_dst = -1;
357                 fd_src = -1;
358
359                 image_list = image_list->next;
360         }
361         _I("=== Image writing end ===");
362
363         return SUCCEED;
364 }
365
366 int main(void)
367 {
368         int ret;
369         _CLEANUP_IMAGE_ struct image *image_list = NULL;
370
371         ret = find_recovery_image();
372         ASSERT_RETV(ret == SUCCEED, ret, "Failed to find recovery image(%d)", ret);
373
374         ret = verify_recovery_image();
375         ASSERT_RETV(ret == SUCCEED, ret, "Failed to verify recovery image (%d)", ret);
376
377         ret = mount_recovery_image();
378         ASSERT_RETV(ret == SUCCEED, ret, "Failed to mount recovery image (%d)", ret);
379
380         ret = run_setup_script();
381         ASSERT_RETV(ret == SUCCEED, ret, "Failed to run setup script (%d)", ret);
382
383         ret = read_config_file(&image_list);
384         ASSERT_RETV(ret == SUCCEED, ret, "Failed to read config file (%d)", ret);
385
386         ret = do_recovery(image_list);
387         ASSERT_RETV(ret == SUCCEED, ret, "Failed to do recovery (%d)", ret);
388
389         _I("Succeed in image recovery");
390         ret = SUCCEED;
391
392         return ret;
393 }