1 /* commands/sysloader/installer/installer.c
3 * Copyright 2008, The Android Open Source Project
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define LOG_TAG "installer"
20 #include <sys/types.h>
28 #include <sys/mount.h>
33 #include <cutils/config_utils.h>
34 #include <cutils/log.h>
36 #include "diskconfig/diskconfig.h"
37 #include "installer.h"
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"
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"
56 fprintf(stderr
, "\t-t - Test mode. Don't write anything to disk.\n");
61 read_conf_file(const char *fn
)
63 cnode
*root
= config_node("", "");
64 config_load_file(root
, fn
);
66 if (root
->first_child
== NULL
) {
67 LOGE("Could not read config file %s", fn
);
75 exec_cmd(const char *cmd
, ...) /* const char *arg, ...) */
83 /* compute the size for the command buffer */
84 size
= strlen(cmd
) + 1;
86 while ((str
= va_arg(ap
, char *))) {
87 size
+= strlen(str
) + 1; /* need room for the space separator */
91 if (!(outbuf
= malloc(size
+ 1))) {
92 LOGE("Can't allocate memory to exec cmd");
96 /* this is a bit inefficient, but is trivial, and works */
99 while ((str
= va_arg(ap
, char *))) {
105 LOGI("Executing: %s", outbuf
);
109 LOGI("Error while trying to execute '%s'", cmd
);
112 rv
= WEXITSTATUS(rv
);
113 LOGI("Done executing %s (%d)", outbuf
, rv
);
119 do_fsck(const char *dst
, int force
)
122 const char *opts
= force
? "-fy" : "-y";
125 LOGI("Running e2fsck... (force=%d) This MAY take a while.", force
);
126 if ((rv
= exec_cmd(E2FSCK_BIN
, "-C 0", opts
, dst
, NULL
)) < 0)
129 LOGE("Error while running e2fsck: %d", rv
);
133 LOGI("e2fsck succeeded (exit code: %d)", rv
);
139 process_ext2_image(const char *dst
, const char *src
, uint32_t flags
, int test
)
143 /* First, write the image to disk. */
144 if (write_raw_image(dst
, src
, 0, test
))
150 /* Next, let's e2fsck the fs to make sure it got written ok, and
151 * everything is peachy */
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)
159 LOGE("Error while running tune2fs: %d", rv
);
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)
168 LOGE("Error while running resize2fs: %d", rv
);
176 /* make this an ext3 fs? */
177 if (flags
& INSTALL_FLAG_ADDJOURNAL
) {
178 if ((rv
= exec_cmd(TUNE2FS_BIN
, "-j", dst
, NULL
)) < 0)
181 LOGE("Error while running tune2fs: %d", rv
);
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 */
197 process_image_node(cnode
*img
, struct disk_info
*dinfo
, int test
)
199 struct part_info
*pinfo
= NULL
;
200 loff_t offset
= (loff_t
)-1;
201 const char *filename
= NULL
;
202 char *dest_part
= NULL
;
209 filename
= config_str(img
, "filename", NULL
);
211 /* process the 'offset' image parameter */
212 if ((tmp
= config_str(img
, "offset", NULL
)) != NULL
)
213 offset
= strtoull(tmp
, NULL
, 0);
215 /* process the 'partition' image parameter */
216 if ((tmp
= config_str(img
, "partition", NULL
)) != NULL
) {
217 if (offset
!= (loff_t
)-1) {
218 LOGE("Cannot specify the partition name AND an offset for %s",
223 if (!(pinfo
= find_part(dinfo
, tmp
))) {
224 LOGE("Cannot find partition %s while processing %s",
229 if (!(dest_part
= find_part_device(dinfo
, pinfo
->name
))) {
230 LOGE("Could not get the device name for partition %s while"
231 " processing image %s", pinfo
->name
, img
->name
);
234 offset
= pinfo
->start_lba
* dinfo
->sect_size
;
237 /* process the 'mkfs' parameter */
238 if ((tmp
= config_str(img
, "mkfs", NULL
)) != NULL
) {
240 char vol_lbl
[16]; /* ext2/3 has a 16-char volume label */
243 LOGE("Target partition required for mkfs for '%s'", img
->name
);
245 } else if (filename
) {
246 LOGE("Providing filename and mkfs parameters is meaningless");
250 if (!strcmp(tmp
, "ext4"))
252 else if (!strcmp(tmp
, "ext2"))
254 else if (!strcmp(tmp
, "ext3"))
257 LOGE("Unknown filesystem type for mkfs: %s", tmp
);
261 /* put the partition name as the volume label */
262 strncpy(vol_lbl
, pinfo
->name
, sizeof(vol_lbl
));
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
);
270 LOGE("Error while running mke2fs: %d", rv
);
274 if (do_fsck(dest_part
, 0))
279 /* since we didn't mkfs above, all the rest of the options assume
280 * there's a filename involved */
282 LOGE("Filename is required for image %s", img
->name
);
286 /* process the 'flags' image parameter */
287 if ((tmp
= config_str(img
, "flags", NULL
)) != NULL
) {
288 char *flagstr
, *flagstr_orig
;
290 if (!(flagstr
= flagstr_orig
= strdup(tmp
))) {
291 LOGE("Cannot allocate memory for dup'd flags string");
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
;
300 LOGE("Unknown flag '%s' for image %s", tmp
, img
->name
);
308 /* process the 'type' image parameter */
309 if (!(tmp
= config_str(img
, "type", NULL
))) {
310 LOGE("Type is required for image %s", img
->name
);
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
;
321 LOGE("Unknown image type '%s' for image %s", tmp
, img
->name
);
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 LOGE("Offset to write into the disk is unknown for %s", img
->name
);
332 if (!pinfo
&& (type
!= INSTALL_IMAGE_RAW
)) {
333 LOGE("Only raw images can specify direct offset on the disk. Please"
334 " specify the target partition name instead. (%s)", img
->name
);
339 case INSTALL_IMAGE_RAW
:
340 if (write_raw_image(dinfo
->device
, filename
, offset
, test
))
344 case INSTALL_IMAGE_EXT3
:
345 /* makes the error checking in the imager function easier */
346 if (flags
& INSTALL_FLAG_ADDJOURNAL
) {
347 LOGW("addjournal flag is meaningless for ext3 images");
348 flags
&= ~INSTALL_FLAG_ADDJOURNAL
;
350 /* ...fall through... */
352 case INSTALL_IMAGE_EXT4
:
355 case INSTALL_IMAGE_EXT2
:
356 if (process_ext2_image(dest_part
, filename
, flags
, test
))
361 LOGE("Unknown image type: %d", type
);
375 main(int argc
, char *argv
[])
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";
386 struct disk_info
*device_disk_info
;
391 while ((x
= getopt (argc
, argv
, "thdc:l:p:")) != EOF
) {
396 inst_conf_file
= optarg
;
399 disk_conf_file
= optarg
;
405 inst_data_dev
= optarg
;
411 fprintf(stderr
, "Unknown argument: %c\n", (char)optopt
);
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
;
421 LOGI("Waiting for device: %s", inst_data_dev
);
422 while (stat(inst_data_dev
, &filestat
))
424 LOGI("Device %s ready", inst_data_dev
);
425 if (mount(inst_data_dev
, inst_data_dir
, data_fstype
, MS_RDONLY
, NULL
)) {
426 LOGE("Could not mount %s on %s as %s", inst_data_dev
, inst_data_dir
,
432 /* Read and process the disk configuration */
433 if (!(device_disk_info
= load_diskconfig(disk_conf_file
, NULL
))) {
434 LOGE("Errors encountered while loading disk conf file %s",
439 if (process_disk_config(device_disk_info
)) {
440 LOGE("Errors encountered while processing disk config from %s",
445 /* Was all of this for educational purposes? If so, quit. */
447 dump_disk_config(device_disk_info
);
451 /* This doesnt do anything but load the config file */
452 if (!(config
= read_conf_file(inst_conf_file
)))
455 /* First, partition the drive */
456 if (apply_disk_config(device_disk_info
, test
))
459 /* Now process the installer config file and write the images to disk */
460 if (!(images
= config_find(config
, "images"))) {
461 LOGE("Invalid configuration file %s. Missing 'images' section",
466 for (img
= images
->first_child
; img
; img
= img
->next
) {
467 if (process_image_node(img
, device_disk_info
, test
)) {
468 LOGE("Unable to write data to partition. Try running 'installer' again.");
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
481 if (apply_disk_config(device_disk_info
, test
))
484 LOGI("Done processing installer config. Configured %d images", cnt
);
485 LOGI("Type 'reboot' or reset to run new image");