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