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.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, "ext2"))
251
            journal_opts = "";
252
        else if (!strcmp(tmp, "ext3"))
253
            journal_opts = "-j";
254
        else {
255
            LOGE("Unknown filesystem type for mkfs: %s", tmp);
256
            goto fail;
257
        }
258
259
        /* put the partition name as the volume label */
260
        strncpy(vol_lbl, pinfo->name, sizeof(vol_lbl));
261
262
        /* since everything checked out, lets make the fs, and return since
263
         * we don't need to do anything else */
264
        rv = exec_cmd(MKE2FS_BIN, "-L", vol_lbl, journal_opts, dest_part, NULL);
265
        if (rv < 0)
266
            goto fail;
267
        else if (rv > 0) {
268
            LOGE("Error while running mke2fs: %d", rv);
269
            goto fail;
270
        }
271
        sync();
272
        if (do_fsck(dest_part, 0))
273
            goto fail;
274
        goto done;
275
    }
276
277
    /* since we didn't mkfs above, all the rest of the options assume
278
     * there's a filename involved */
279
    if (!filename) {
280
        LOGE("Filename is required for image %s", img->name);
281
        goto fail;
282
    }
283
284
    /* process the 'flags' image parameter */
285
    if ((tmp = config_str(img, "flags", NULL)) != NULL) {
286
        char *flagstr, *flagstr_orig;
287
288
        if (!(flagstr = flagstr_orig = strdup(tmp))) {
289
            LOGE("Cannot allocate memory for dup'd flags string");
290
            goto fail;
291
        }
292
        while ((tmp = strsep(&flagstr, ","))) {
293
            if (!strcmp(tmp, "resize"))
294
                flags |= INSTALL_FLAG_RESIZE;
295
            else if (!strcmp(tmp, "addjournal"))
296
                flags |= INSTALL_FLAG_ADDJOURNAL;
297
            else {
298
                LOGE("Unknown flag '%s' for image %s", tmp, img->name);
299
                free(flagstr_orig);
300
                goto fail;
301
            }
302
        }
303
        free(flagstr_orig);
304
    }
305
306
    /* process the 'type' image parameter */
307
    if (!(tmp = config_str(img, "type", NULL))) {
308
        LOGE("Type is required for image %s", img->name);
309
        goto fail;
310
    } else if (!strcmp(tmp, "raw")) {
311
        type = INSTALL_IMAGE_RAW;
312
    } else if (!strcmp(tmp, "ext2")) {
313
        type = INSTALL_IMAGE_EXT2;
314
    } else if (!strcmp(tmp, "ext3")) {
315
        type = INSTALL_IMAGE_EXT3;
316
    } else {
317
        LOGE("Unknown image type '%s' for image %s", tmp, img->name);
318
        goto fail;
319
    }
320
321
    /* at this point we MUST either have a partition in 'pinfo' or a raw
322
     * 'offset', otherwise quit */
323
    if (!pinfo && (offset == (loff_t)-1)) {
324
        LOGE("Offset to write into the disk is unknown for %s", img->name);
325
        goto fail;
326
    }
327
328
    if (!pinfo && (type != INSTALL_IMAGE_RAW)) {
329
        LOGE("Only raw images can specify direct offset on the disk. Please"
330
             " specify the target partition name instead. (%s)", img->name);
331
        goto fail;
332
    }
333
334
    switch(type) {
335
        case INSTALL_IMAGE_RAW:
336
            if (write_raw_image(dinfo->device, filename, offset, test))
337
                goto fail;
338
            break;
339
340
        case INSTALL_IMAGE_EXT3:
341
            /* makes the error checking in the imager function easier */
342
            if (flags & INSTALL_FLAG_ADDJOURNAL) {
343
                LOGW("addjournal flag is meaningless for ext3 images");
344
                flags &= ~INSTALL_FLAG_ADDJOURNAL;
345
            }
346
            /* ...fall through... */
347
348
        case INSTALL_IMAGE_EXT2:
349
            if (process_ext2_image(dest_part, filename, flags, test))
350
                goto fail;
351
            break;
352
353
        default:
354
            LOGE("Unknown image type: %d", type);
355
            goto fail;
356
    }
357
358
done:
359
    func_ret = 0;
360
361
fail:
362
    if (dest_part)
363
        free(dest_part);
364
    return func_ret;
365
}
366
367
int
368
main(int argc, char *argv[])
369
{
370
    char *disk_conf_file = "/system/etc/disk_layout.conf";
371
    char *inst_conf_file = "/system/etc/installer.conf";
372
    char *inst_data_dir = "/data";
373
    char *inst_data_dev = NULL;
374
    char *data_fstype = "ext2";
375
    cnode *config;
376
    cnode *images;
377
    cnode *img;
378
    int cnt = 0;
379
    struct disk_info *device_disk_info;
380
    int dump = 0;
381
    int test = 0;
382
    int x;
383
384
    while ((x = getopt (argc, argv, "thdc:l:p:")) != EOF) {
385
        switch (x) {
386
            case 'h':
387
                return usage();
388
            case 'c':
389
                inst_conf_file = optarg;
390
                break;
391
            case 'l':
392
                disk_conf_file = optarg;
393
                break;
394
            case 't':
395
                test = 1;
396
                break;
397
            case 'p':
398
                inst_data_dev = optarg;
399
                break;
400
            case 'd':
401
                dump = 1;
402
                break;
403
            default:
404
                fprintf(stderr, "Unknown argument: %c\n", (char)optopt);
405
                return usage();
406
        }
407
    }
408
409
    /* If the user asked us to wait for data device, wait for it to appear,
410
     * and then mount it onto /data */
411
    if (inst_data_dev && !dump) {
412
        struct stat filestat;
413
414
        LOGI("Waiting for device: %s", inst_data_dev);
415
        while (stat(inst_data_dev, &filestat))
416
            sleep(1);
417
        LOGI("Device %s ready", inst_data_dev);
418
        if (mount(inst_data_dev, inst_data_dir, data_fstype, MS_RDONLY, NULL)) {
419
            LOGE("Could not mount %s on %s as %s", inst_data_dev, inst_data_dir,
420
                 data_fstype);
421
            return 1;
422
        }
423
    }
424
425
    /* Read and process the disk configuration */
426
    if (!(device_disk_info = load_diskconfig(disk_conf_file, NULL))) {
427
        LOGE("Errors encountered while loading disk conf file %s",
428
             disk_conf_file);
429
        return 1;
430
    }
431
432
    if (process_disk_config(device_disk_info)) {
433
        LOGE("Errors encountered while processing disk config from %s",
434
             disk_conf_file);
435
        return 1;
436
    }
437
438
    /* Was all of this for educational purposes? If so, quit. */
439
    if (dump) {
440
        dump_disk_config(device_disk_info);
441
        return 0;
442
    }
443
444
    /* This doesnt do anything but load the config file */
445
    if (!(config = read_conf_file(inst_conf_file)))
446
        return 1;
447
448
    /* First, partition the drive */
449
    if (apply_disk_config(device_disk_info, test))
450
        return 1;
451
452
    /* Now process the installer config file and write the images to disk */
453
    if (!(images = config_find(config, "images"))) {
454
        LOGE("Invalid configuration file %s. Missing 'images' section",
455
             inst_conf_file);
456
        return 1;
457
    }
458
459
    for (img = images->first_child; img; img = img->next) {
460
        if (process_image_node(img, device_disk_info, test))
461
            return 1;
462
        ++cnt;
463
    }
464
465
    /*
466
     * We have to do the apply() twice. We must do it once before the image
467
     * writes to layout the disk partitions so that we can write images to
468
     * them. We then do the apply() again in case one of the images
469
     * replaced the MBR with a new bootloader, and thus messed with
470
     * partition table.
471
     */
472
    if (apply_disk_config(device_disk_info, test))
473
        return 1;
474
475
    LOGI("Done processing installer config. Configured %d images", cnt);
476
    return 0;
477
}