Home | History | Annotate | Download | only in diskinstaller
      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
     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 *
     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         LOGE("Could not read config file %s", fn);
     68         return NULL;
     69     }
     70 
     71     return root;
     72 }
     73 
     74 static int
     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         LOGE("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     LOGI("Executing: %s", outbuf);
    106     rv = system(outbuf);
    107     free(outbuf);
    108     if (rv < 0) {
    109         LOGI("Error while trying to execute '%s'", cmd);
    110         return -1;
    111     }
    112     rv = WEXITSTATUS(rv);
    113     LOGI("Done executing %s (%d)", outbuf, rv);
    114     return rv;
    115 }
    116 
    117 
    118 static int
    119 do_fsck(const char *dst, int force)
    120 {
    121     int rv;
    122     const char *opts = force ? "-fy" : "-y";
    123 
    124 
    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)
    127         return 1;
    128     if (rv >= 4) {
    129         LOGE("Error while running e2fsck: %d", rv);
    130         return 1;
    131     }
    132     sync();
    133     LOGI("e2fsck succeeded (exit code: %d)", rv);
    134 
    135     return 0;
    136 }
    137 
    138 static int
    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         LOGE("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             LOGE("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             LOGE("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
    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             LOGE("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             LOGE("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             LOGE("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             LOGE("Target partition required for mkfs for '%s'", img->name);
    244             goto fail;
    245         } else if (filename) {
    246             LOGE("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             LOGE("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             LOGE("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         LOGE("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             LOGE("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                 LOGE("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         LOGE("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         LOGE("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         LOGE("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         LOGE("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                 LOGW("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             LOGE("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
    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         LOGI("Waiting for device: %s", inst_data_dev);
    422         while (stat(inst_data_dev, &filestat))
    423             sleep(1);
    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,
    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         LOGE("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         LOGE("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         LOGE("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             LOGE("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     LOGI("Done processing installer config. Configured %d images", cnt);
    485     LOGI("Type 'reboot' or reset to run new image");
    486     return 0;
    487 }
    488