• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* commands/sysloader/installer/installer.c
2  *
3  * Copyright 2008, The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #define LOG_TAG "installer"
19 
20 #include <sys/types.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/mount.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 
32 
33 #include <cutils/config_utils.h>
34 #include <cutils/log.h>
35 
36 #include "diskconfig/diskconfig.h"
37 #include "installer.h"
38 
39 #define MKE2FS_BIN     "/system/bin/mke2fs"
40 #define E2FSCK_BIN     "/system/bin/e2fsck"
41 #define TUNE2FS_BIN    "/system/bin/tune2fs"
42 #define RESIZE2FS_BIN  "/system/bin/resize2fs"
43 
44 static int
usage(void)45 usage(void)
46 {
47     fprintf(stderr, "Usage: %s\n", LOG_TAG);
48     fprintf(stderr, "\t-c <path> - Path to installer conf file "
49                     "(/system/etc/installer.conf)\n");
50     fprintf(stderr, "\t-l <path> - Path to device disk layout conf file "
51                     "(/system/etc/disk_layout.conf)\n");
52     fprintf(stderr, "\t-h        - This help message\n");
53     fprintf(stderr, "\t-d        - Dump the compiled in partition info.\n");
54     fprintf(stderr, "\t-p <path> - Path to device that should be mounted"
55                     " to /data.\n");
56     fprintf(stderr, "\t-t        - Test mode. Don't write anything to disk.\n");
57     return 1;
58 }
59 
60 static cnode *
read_conf_file(const char * fn)61 read_conf_file(const char *fn)
62 {
63     cnode *root = config_node("", "");
64     config_load_file(root, fn);
65 
66     if (root->first_child == NULL) {
67         ALOGE("Could not read config file %s", fn);
68         return NULL;
69     }
70 
71     return root;
72 }
73 
74 static int
exec_cmd(const char * cmd,...)75 exec_cmd(const char *cmd, ...) /* const char *arg, ...) */
76 {
77     va_list ap;
78     int size = 0;
79     char *str;
80     char *outbuf;
81     int rv;
82 
83     /* compute the size for the command buffer */
84     size = strlen(cmd) + 1;
85     va_start(ap, cmd);
86     while ((str = va_arg(ap, char *))) {
87         size += strlen(str) + 1;  /* need room for the space separator */
88     }
89     va_end(ap);
90 
91     if (!(outbuf = malloc(size + 1))) {
92         ALOGE("Can't allocate memory to exec cmd");
93         return -1;
94     }
95 
96     /* this is a bit inefficient, but is trivial, and works */
97     strcpy(outbuf, cmd);
98     va_start(ap, cmd);
99     while ((str = va_arg(ap, char *))) {
100         strcat(outbuf, " ");
101         strcat(outbuf, str);
102     }
103     va_end(ap);
104 
105     ALOGI("Executing: %s", outbuf);
106     rv = system(outbuf);
107     free(outbuf);
108     if (rv < 0) {
109         ALOGI("Error while trying to execute '%s'", cmd);
110         return -1;
111     }
112     rv = WEXITSTATUS(rv);
113     ALOGI("Done executing %s (%d)", outbuf, rv);
114     return rv;
115 }
116 
117 
118 static int
do_fsck(const char * dst,int force)119 do_fsck(const char *dst, int force)
120 {
121     int rv;
122     const char *opts = force ? "-fy" : "-y";
123 
124 
125     ALOGI("Running e2fsck... (force=%d) This MAY take a while.", force);
126     if ((rv = exec_cmd(E2FSCK_BIN, "-C 0", opts, dst, NULL)) < 0)
127         return 1;
128     if (rv >= 4) {
129         ALOGE("Error while running e2fsck: %d", rv);
130         return 1;
131     }
132     sync();
133     ALOGI("e2fsck succeeded (exit code: %d)", rv);
134 
135     return 0;
136 }
137 
138 static int
process_ext2_image(const char * dst,const char * src,uint32_t flags,int test)139 process_ext2_image(const char *dst, const char *src, uint32_t flags, int test)
140 {
141     int rv;
142 
143     /* First, write the image to disk. */
144     if (write_raw_image(dst, src, 0, test))
145         return 1;
146 
147     if (test)
148         return 0;
149 
150     /* Next, let's e2fsck the fs to make sure it got written ok, and
151      * everything is peachy */
152     if (do_fsck(dst, 1))
153         return 1;
154 
155     /* set the mount count to 1 so that 1st mount on boot doesn't complain */
156     if ((rv = exec_cmd(TUNE2FS_BIN, "-C", "1", dst, NULL)) < 0)
157         return 1;
158     if (rv) {
159         ALOGE("Error while running tune2fs: %d", rv);
160         return 1;
161     }
162 
163     /* If the user requested that we resize, let's do it now */
164     if (flags & INSTALL_FLAG_RESIZE) {
165         if ((rv = exec_cmd(RESIZE2FS_BIN, "-F", dst, NULL)) < 0)
166             return 1;
167         if (rv) {
168             ALOGE("Error while running resize2fs: %d", rv);
169             return 1;
170         }
171         sync();
172         if (do_fsck(dst, 0))
173             return 1;
174     }
175 
176     /* make this an ext3 fs? */
177     if (flags & INSTALL_FLAG_ADDJOURNAL) {
178         if ((rv = exec_cmd(TUNE2FS_BIN, "-j", dst, NULL)) < 0)
179             return 1;
180         if (rv) {
181             ALOGE("Error while running tune2fs: %d", rv);
182             return 1;
183         }
184         sync();
185         if (do_fsck(dst, 0))
186             return 1;
187     }
188 
189     return 0;
190 }
191 
192 
193 /* TODO: PLEASE break up this function into several functions that just
194  * do what they need with the image node. Many of them will end up
195  * looking at same strings, but it will be sooo much cleaner */
196 static int
process_image_node(cnode * img,struct disk_info * dinfo,int test)197 process_image_node(cnode *img, struct disk_info *dinfo, int test)
198 {
199     struct part_info *pinfo = NULL;
200     loff_t offset = (loff_t)-1;
201     const char *filename = NULL;
202     char *dest_part = NULL;
203     const char *tmp;
204     uint32_t flags = 0;
205     uint8_t type = 0;
206     int rv;
207     int func_ret = 1;
208 
209     filename = config_str(img, "filename", NULL);
210 
211     /* process the 'offset' image parameter */
212     if ((tmp = config_str(img, "offset", NULL)) != NULL)
213         offset = strtoull(tmp, NULL, 0);
214 
215     /* process the 'partition' image parameter */
216     if ((tmp = config_str(img, "partition", NULL)) != NULL) {
217         if (offset != (loff_t)-1) {
218             ALOGE("Cannot specify the partition name AND an offset for %s",
219                  img->name);
220             goto fail;
221         }
222 
223         if (!(pinfo = find_part(dinfo, tmp))) {
224             ALOGE("Cannot find partition %s while processing %s",
225                  tmp, img->name);
226             goto fail;
227         }
228 
229         if (!(dest_part = find_part_device(dinfo, pinfo->name))) {
230             ALOGE("Could not get the device name for partition %s while"
231                  " processing image %s", pinfo->name, img->name);
232             goto fail;
233         }
234         offset = pinfo->start_lba * dinfo->sect_size;
235     }
236 
237     /* process the 'mkfs' parameter */
238     if ((tmp = config_str(img, "mkfs", NULL)) != NULL) {
239         char *journal_opts;
240         char vol_lbl[16]; /* ext2/3 has a 16-char volume label */
241 
242         if (!pinfo) {
243             ALOGE("Target partition required for mkfs for '%s'", img->name);
244             goto fail;
245         } else if (filename) {
246             ALOGE("Providing filename and mkfs parameters is meaningless");
247             goto fail;
248         }
249 
250         if (!strcmp(tmp, "ext4"))
251             journal_opts = "";
252         else if (!strcmp(tmp, "ext2"))
253             journal_opts = "";
254         else if (!strcmp(tmp, "ext3"))
255             journal_opts = "-j";
256         else {
257             ALOGE("Unknown filesystem type for mkfs: %s", tmp);
258             goto fail;
259         }
260 
261         /* put the partition name as the volume label */
262         strncpy(vol_lbl, pinfo->name, sizeof(vol_lbl));
263 
264         /* since everything checked out, lets make the fs, and return since
265          * we don't need to do anything else */
266         rv = exec_cmd(MKE2FS_BIN, "-L", vol_lbl, journal_opts, dest_part, NULL);
267         if (rv < 0)
268             goto fail;
269         else if (rv > 0) {
270             ALOGE("Error while running mke2fs: %d", rv);
271             goto fail;
272         }
273         sync();
274         if (do_fsck(dest_part, 0))
275             goto fail;
276         goto done;
277     }
278 
279     /* since we didn't mkfs above, all the rest of the options assume
280      * there's a filename involved */
281     if (!filename) {
282         ALOGE("Filename is required for image %s", img->name);
283         goto fail;
284     }
285 
286     /* process the 'flags' image parameter */
287     if ((tmp = config_str(img, "flags", NULL)) != NULL) {
288         char *flagstr, *flagstr_orig;
289 
290         if (!(flagstr = flagstr_orig = strdup(tmp))) {
291             ALOGE("Cannot allocate memory for dup'd flags string");
292             goto fail;
293         }
294         while ((tmp = strsep(&flagstr, ","))) {
295             if (!strcmp(tmp, "resize"))
296                 flags |= INSTALL_FLAG_RESIZE;
297             else if (!strcmp(tmp, "addjournal"))
298                 flags |= INSTALL_FLAG_ADDJOURNAL;
299             else {
300                 ALOGE("Unknown flag '%s' for image %s", tmp, img->name);
301                 free(flagstr_orig);
302                 goto fail;
303             }
304         }
305         free(flagstr_orig);
306     }
307 
308     /* process the 'type' image parameter */
309     if (!(tmp = config_str(img, "type", NULL))) {
310         ALOGE("Type is required for image %s", img->name);
311         goto fail;
312     } else if (!strcmp(tmp, "raw")) {
313         type = INSTALL_IMAGE_RAW;
314     } else if (!strcmp(tmp, "ext2")) {
315         type = INSTALL_IMAGE_EXT2;
316     } else if (!strcmp(tmp, "ext3")) {
317         type = INSTALL_IMAGE_EXT3;
318     } else if (!strcmp(tmp, "ext4")) {
319         type = INSTALL_IMAGE_EXT4;
320     } else {
321         ALOGE("Unknown image type '%s' for image %s", tmp, img->name);
322         goto fail;
323     }
324 
325     /* at this point we MUST either have a partition in 'pinfo' or a raw
326      * 'offset', otherwise quit */
327     if (!pinfo && (offset == (loff_t)-1)) {
328         ALOGE("Offset to write into the disk is unknown for %s", img->name);
329         goto fail;
330     }
331 
332     if (!pinfo && (type != INSTALL_IMAGE_RAW)) {
333         ALOGE("Only raw images can specify direct offset on the disk. Please"
334              " specify the target partition name instead. (%s)", img->name);
335         goto fail;
336     }
337 
338     switch(type) {
339         case INSTALL_IMAGE_RAW:
340             if (write_raw_image(dinfo->device, filename, offset, test))
341                 goto fail;
342             break;
343 
344         case INSTALL_IMAGE_EXT3:
345             /* makes the error checking in the imager function easier */
346             if (flags & INSTALL_FLAG_ADDJOURNAL) {
347                 ALOGW("addjournal flag is meaningless for ext3 images");
348                 flags &= ~INSTALL_FLAG_ADDJOURNAL;
349             }
350             /* ...fall through... */
351 
352         case INSTALL_IMAGE_EXT4:
353             /* fallthru */
354 
355         case INSTALL_IMAGE_EXT2:
356             if (process_ext2_image(dest_part, filename, flags, test))
357                 goto fail;
358             break;
359 
360         default:
361             ALOGE("Unknown image type: %d", type);
362             goto fail;
363     }
364 
365 done:
366     func_ret = 0;
367 
368 fail:
369     if (dest_part)
370         free(dest_part);
371     return func_ret;
372 }
373 
374 int
main(int argc,char * argv[])375 main(int argc, char *argv[])
376 {
377     char *disk_conf_file = "/system/etc/disk_layout.conf";
378     char *inst_conf_file = "/system/etc/installer.conf";
379     char *inst_data_dir = "/data";
380     char *inst_data_dev = NULL;
381     char *data_fstype = "ext4";
382     cnode *config;
383     cnode *images;
384     cnode *img;
385     int cnt = 0;
386     struct disk_info *device_disk_info;
387     int dump = 0;
388     int test = 0;
389     int x;
390 
391     while ((x = getopt (argc, argv, "thdc:l:p:")) != EOF) {
392         switch (x) {
393             case 'h':
394                 return usage();
395             case 'c':
396                 inst_conf_file = optarg;
397                 break;
398             case 'l':
399                 disk_conf_file = optarg;
400                 break;
401             case 't':
402                 test = 1;
403                 break;
404             case 'p':
405                 inst_data_dev = optarg;
406                 break;
407             case 'd':
408                 dump = 1;
409                 break;
410             default:
411                 fprintf(stderr, "Unknown argument: %c\n", (char)optopt);
412                 return usage();
413         }
414     }
415 
416     /* If the user asked us to wait for data device, wait for it to appear,
417      * and then mount it onto /data */
418     if (inst_data_dev && !dump) {
419         struct stat filestat;
420 
421         ALOGI("Waiting for device: %s", inst_data_dev);
422         while (stat(inst_data_dev, &filestat))
423             sleep(1);
424         ALOGI("Device %s ready", inst_data_dev);
425         if (mount(inst_data_dev, inst_data_dir, data_fstype, MS_RDONLY, NULL)) {
426             ALOGE("Could not mount %s on %s as %s", inst_data_dev, inst_data_dir,
427                  data_fstype);
428             return 1;
429         }
430     }
431 
432     /* Read and process the disk configuration */
433     if (!(device_disk_info = load_diskconfig(disk_conf_file, NULL))) {
434         ALOGE("Errors encountered while loading disk conf file %s",
435              disk_conf_file);
436         return 1;
437     }
438 
439     if (process_disk_config(device_disk_info)) {
440         ALOGE("Errors encountered while processing disk config from %s",
441              disk_conf_file);
442         return 1;
443     }
444 
445     /* Was all of this for educational purposes? If so, quit. */
446     if (dump) {
447         dump_disk_config(device_disk_info);
448         return 0;
449     }
450 
451     /* This doesnt do anything but load the config file */
452     if (!(config = read_conf_file(inst_conf_file)))
453         return 1;
454 
455     /* First, partition the drive */
456     if (apply_disk_config(device_disk_info, test))
457         return 1;
458 
459     /* Now process the installer config file and write the images to disk */
460     if (!(images = config_find(config, "images"))) {
461         ALOGE("Invalid configuration file %s. Missing 'images' section",
462              inst_conf_file);
463         return 1;
464     }
465 
466     for (img = images->first_child; img; img = img->next) {
467         if (process_image_node(img, device_disk_info, test)) {
468             ALOGE("Unable to write data to partition. Try running 'installer' again.");
469             return 1;
470         }
471         ++cnt;
472     }
473 
474     /*
475      * We have to do the apply() twice. We must do it once before the image
476      * writes to layout the disk partitions so that we can write images to
477      * them. We then do the apply() again in case one of the images
478      * replaced the MBR with a new bootloader, and thus messed with
479      * partition table.
480      */
481     if (apply_disk_config(device_disk_info, test))
482         return 1;
483 
484     ALOGI("Done processing installer config. Configured %d images", cnt);
485     ALOGI("Type 'reboot' or reset to run new image");
486     return 0;
487 }
488