1/**
2 * \file
3 * \brief ramfs service
4 */
5
6/*
7 * Copyright (c) 2010, 2011, 2013, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#define _USE_XOPEN // for strdup()
16#include <stdio.h>
17#include <string.h>
18#include <zlib.h>
19#include <barrelfish/barrelfish.h>
20#include <barrelfish/nameservice_client.h>
21#include <if/monitor_defs.h>
22#include <if/monitor_blocking_defs.h>
23#include <if/trivfs_defs.h>
24#include <spawndomain/spawndomain.h>
25#include "ramfs.h"
26#include <cpiobin.h>
27
28#define BOOTSCRIPT_FILE_NAME "bootmodules"
29
30static errval_t write_directory(struct dirent *root, const char *path);
31static errval_t write_file(struct dirent *root, const char *path, uint8_t *data,
32                           size_t len);
33
34/* -------------------- CODE TO UNPACK RAMFS IMAGES  ------------------------ */
35
36static int cpio_entry_handler(int n, const cpio_generic_header_t *h, void *arg)
37{
38    struct dirent *root = arg;
39    errval_t err;
40
41    if(CPIO_MODE_FILE == (h->mode & CPIO_MODE_FILE_TYPE_MASK)) {
42        err = write_file(root, h->name, (void *)h->data, h->datasize);
43    } else if(CPIO_MODE_DIRECTORY == (h->mode & CPIO_MODE_FILE_TYPE_MASK)) {
44        err = write_directory(root, h->name);
45    } else {
46        return 0;
47    }
48
49    if (err_is_fail(err)) {
50        USER_PANIC_ERR(err, "error writing file to ramfs");
51    }
52
53    return 0;
54}
55
56static errval_t unpack_cpio(struct dirent *root, void *data, size_t len)
57{
58    if (!cpio_archive_valid(data, len)) {
59        USER_PANIC("invalid CPIO archive");
60    }
61
62    cpio_generic_header_t h;
63    cpio_visit(data, len, cpio_entry_handler, &h, root);
64    return SYS_ERR_OK;
65}
66
67static errval_t unpack_cpiogz(struct dirent *root, void *data, size_t len)
68{
69    /* decompress the image to a local buffer */
70    static uint8_t chunk[16384];
71    z_stream d_stream; /* decompression stream */
72    errval_t err;
73    int zerr;
74
75    size_t outbuflen = 16384, outbufpos = 0;
76    uint8_t *outbuf = malloc(outbuflen);
77    assert(outbuf != NULL);
78
79    memset(&d_stream, 0, sizeof(d_stream));
80    d_stream.next_in  = data;
81    d_stream.avail_in = len;
82
83    zerr = inflateInit2(&d_stream, MAX_WBITS + 32);
84    assert(zerr == Z_OK);
85
86    do {
87        do {
88            d_stream.avail_out = sizeof(chunk);
89            d_stream.next_out = chunk;
90
91            zerr = inflate(&d_stream, Z_NO_FLUSH);
92            if (zerr < 0 || zerr == Z_NEED_DICT) {
93                debug_printf("zlib error %d: %s\n", zerr, d_stream.msg);
94                abort(); // FIXME
95            }
96
97            size_t outchunklen = sizeof(chunk) - d_stream.avail_out;
98            while (outchunklen + outbufpos > outbuflen) {
99                // XXX: inefficient: grow output buffer, copying everything
100                outbuflen *= 2;
101                outbuf = realloc(outbuf, outbuflen);
102                assert(outbuf != NULL);
103            }
104            memcpy(&outbuf[outbufpos], chunk, outchunklen);
105            outbufpos += outchunklen;
106        } while(d_stream.avail_out == 0);
107        assert(zerr == Z_STREAM_END);
108    } while (zerr != Z_STREAM_END);
109
110    zerr = inflateEnd(&d_stream);
111    assert(zerr == Z_OK);
112
113    assert(outbufpos == d_stream.total_out);
114    err = unpack_cpio(root, outbuf, outbufpos);
115    free(outbuf);
116    return err;
117}
118
119/* ---------------------- POPULATION FROM BOOTINFO  ------------------------- */
120
121static errval_t write_directory(struct dirent *root, const char *path)
122{
123    errval_t err;
124
125    assert(path != NULL);
126    assert(root != NULL);
127
128    // walk path, creating/locating directories as we go
129    struct dirent *d = root;
130    while (true) {
131        // remove any leading /
132        while (path[0] == '/') {
133            path++;
134        }
135
136        char *nextsep = strchr(path, '/');
137
138        // no more /, we have the directory name
139        if (nextsep == NULL) {
140            break;
141        }
142
143        // extract dirname, advance path
144        size_t namelen = nextsep - path;
145        char *dirname = malloc(namelen + 1);
146        assert(dirname != NULL);
147        memcpy(dirname, path, namelen);
148        dirname[namelen] = '\0';
149        path += namelen;
150
151        // does this directory exist?
152        struct dirent *next;
153        err = ramfs_lookup(d, dirname, &next);
154        if (err_is_ok(err)) {
155            free(dirname);
156            d = next;
157            continue;
158        } else if (err_no(err) != FS_ERR_NOTFOUND) {
159            free(dirname);
160            return err;
161        }
162
163        // create the directory
164        err = ramfs_mkdir(d, dirname, &next);
165        if (err_is_fail(err)) {
166            free(dirname);
167            return err;
168        }
169        d = next;
170    }
171
172    // create the last-level directory
173    char *path_copy = strdup(path);
174    assert(path_copy != NULL);
175    err = ramfs_mkdir(d, path_copy, NULL);
176    if (err_is_fail(err)) {
177        free(path_copy);
178        return err;
179    }
180
181    return SYS_ERR_OK;
182}
183
184static errval_t write_file(struct dirent *root, const char *path, uint8_t *data,
185                           size_t len)
186{
187    errval_t err;
188
189    assert(path != NULL);
190    assert(root != NULL);
191
192    // walk path, creating/locating directories as we go
193    struct dirent *d = root;
194    while (true) {
195        // remove any leading /
196        while (path[0] == '/') {
197            path++;
198        }
199
200        char *nextsep = strchr(path, '/');
201
202        // no more /, we have the filename
203        if (nextsep == NULL) {
204            break;
205        }
206
207        // extract dirname, advance path
208        size_t namelen = nextsep - path;
209        char *dirname = malloc(namelen + 1);
210        assert(dirname != NULL);
211        memcpy(dirname, path, namelen);
212        dirname[namelen] = '\0';
213        path += namelen;
214
215        // does this directory exist?
216        struct dirent *next;
217        err = ramfs_lookup(d, dirname, &next);
218        if (err_is_ok(err)) {
219            free(dirname);
220            d = next;
221            continue;
222        } else if (err_no(err) != FS_ERR_NOTFOUND) {
223            free(dirname);
224            return err;
225        }
226
227        // create the directory
228        err = ramfs_mkdir(d, dirname, &next);
229        if (err_is_fail(err)) {
230            free(dirname);
231            return err;
232        }
233        d = next;
234    }
235
236    // create the file!
237    struct dirent *f;
238    char *path_copy = strdup(path);
239    assert(path_copy != NULL);
240    err = ramfs_create(d, path_copy, &f);
241    if (err_is_fail(err)) {
242        free(path_copy);
243        return err;
244    }
245
246    // allocate storage
247    uint8_t *buf;
248    err = ramfs_grow(f, 0, len, &buf);
249    if (err_is_fail(err)) {
250        ramfs_delete(f);
251        return err;
252    }
253
254    // copy the payload
255    memcpy(buf, data, len);
256
257    return SYS_ERR_OK;
258}
259
260static errval_t append_to_file(struct dirent *f, const char *str)
261{
262    assert(!ramfs_isdir(f));
263    size_t pos = ramfs_get_size(f);
264    size_t len = strlen(str);
265    errval_t err;
266
267    // allocate storage
268    uint8_t *buf;
269    err = ramfs_grow(f, pos, len + 1, &buf);
270    if (err_is_fail(err)) {
271        return err;
272    }
273
274    // copy the payload
275    memcpy(buf, str, len);
276
277    // terminate with a \n
278    buf[len] = '\n';
279
280    return SYS_ERR_OK;
281}
282
283// try to remove the 'irrelevant' prefix of a multiboot path
284// eg. "/username/blerg/x86_64/sbin/foo..." becomes "/x86_64/sbin/foo..."
285static const char *remove_prefix(const char *path)
286{
287    static char *theprefix;
288    static size_t theprefixlen;
289
290    // don't know anything
291    if (theprefix == NULL) {
292        // walk forward until we see '/sbin/'
293        char *sbin = strstr(path, "/sbin/");
294        if (sbin == NULL || sbin == path) {
295            return path; // give up
296        }
297
298        // walk backward one path element (the architecture)
299        char *prevsep = memrchr(path, '/', sbin - path);
300        if (prevsep == NULL) {
301            return path; // give up
302        }
303
304        // store copy of prefix for future use
305        theprefixlen = prevsep - path;
306        theprefix = malloc(theprefixlen + 1);
307        assert(theprefix != NULL);
308        memcpy(theprefix, path, theprefixlen);
309        theprefix[theprefixlen] = '\0';
310
311        return prevsep;
312    } else if (strncmp(theprefix, path, theprefixlen) == 0) {
313        // check if stored prefix matches
314        return &path[theprefixlen];
315    } else { // no match?
316        debug_printf("remove_prefix(): '%s' doesn't match stored prefix '%s'\n",
317                     path, theprefix);
318        return path;
319    }
320}
321
322static errval_t getimage(struct mem_region *region, size_t *retlen,
323                         lvaddr_t *retdata)
324{
325    // map in the real module (FIXME: leaking it afterwards)
326    size_t len;
327    errval_t err = spawn_map_module(region, &len, retdata, NULL);
328    if (err_is_ok(err)) {
329        assert(len >= region->mrmod_size);
330        *retlen = region->mrmod_size;
331    }
332    return err;
333}
334
335static void populate_multiboot(struct dirent *root, struct bootinfo *bi)
336{
337    lvaddr_t data;
338    size_t len;
339    errval_t err;
340
341    assert(root != NULL);
342
343    // create bootscript file
344    struct dirent *bootscript_f;
345    err = ramfs_create(root, BOOTSCRIPT_FILE_NAME, &bootscript_f);
346    if (err_is_fail(err)) {
347        USER_PANIC_ERR(err, "error creating bootscript file");
348    }
349
350    debug_printf("pre-populating from boot image...\n");
351
352    for (int i = 0; i < bi->regions_length; i++) {
353        struct mem_region *region = &bi->regions[i];
354        if (region->mr_type != RegionType_Module) { /* Not of module type */
355            continue;
356        }
357
358        const char *name = remove_prefix(multiboot_module_name(region));
359
360        // is this a ramfs image we should unpack?
361        if (strstr(name, "_ramfs.cpio.gz") != NULL) {
362            debug_printf("unpacking Gzipped CPIO %s\n", name);
363            err = spawn_map_module(region, &len, &data, NULL);
364            if (err_is_fail(err)) {
365                USER_PANIC_ERR(err, "error in spawn_map_module");
366            }
367
368            err = unpack_cpiogz(root, (void *)data, len);
369            if (err_is_fail(err)) {
370                USER_PANIC_ERR(err, "error unpacking ramfs image");
371            }
372
373            // TODO: unmap
374        } else if (strstr(name, "_ramfs.cpio") != NULL) {
375            debug_printf("unpacking CPIO %s\n", name);
376            err = spawn_map_module(region, &len, &data, NULL);
377            if (err_is_fail(err)) {
378                USER_PANIC_ERR(err, "error in spawn_map_module");
379            }
380
381            err = unpack_cpio(root, (void *)data, len);
382            if (err_is_fail(err)) {
383                USER_PANIC_ERR(err, "error unpacking ramfs image");
384            }
385
386            // TODO: unmap
387        } else {
388            // map the image
389            err = getimage(region, &len, &data);
390            if (err_is_fail(err)) {
391                USER_PANIC_ERR(err, "error in getimage");
392            }
393
394            // copy to ramfs
395            err = write_file(root, name, (void *)data, len);
396            if (err_is_fail(err)) {
397                if (err_no(err) == FS_ERR_EXISTS) {
398                    debug_printf("%s already exists, skipping it\n", name);
399                } else {
400                    USER_PANIC_ERR(err, "error in write_file");
401                }
402            }
403
404            // TODO: unmap
405
406            // append line to bootscript
407            const char *args = remove_prefix(multiboot_module_rawstring(region));
408            char *line = NULL;
409
410            // Prepend a '/' if path is relative
411            if(args[0] != '/') {
412                line = calloc(strlen(args) + 2, 1);
413                line[0] = '/';
414                strcat(line, args);
415                args = line;
416            }
417
418            err = append_to_file(bootscript_f, args);
419
420            // Free temporary buffer if allocated
421            if(line != NULL) {
422                free(line);
423            }
424
425            if (err_is_fail(err)) {
426                USER_PANIC_ERR(err, "error appending to bootscript");
427            }
428        }
429    }
430
431    debug_printf("ready\n");
432}
433
434// Get the bootinfo and map it in.
435static errval_t map_bootinfo(struct bootinfo **bootinfo)
436{
437    errval_t err, msgerr;
438
439    struct monitor_blocking_binding *cl = get_monitor_blocking_binding();
440    assert(cl != NULL);
441
442    struct capref bootinfo_frame;
443    size_t bootinfo_size;
444
445    err = slot_alloc(&bootinfo_frame);
446    if (err_is_fail(err)) {
447        return err;
448    }
449    msgerr = cl->rpc_tx_vtbl.get_bootinfo(cl, &err, &bootinfo_frame, &bootinfo_size);
450    if (err_is_fail(msgerr)) {
451        err = msgerr;
452    }
453    if (err_is_fail(err)) {
454        USER_PANIC_ERR(err, "failed in get_bootinfo");
455        return err;
456    }
457
458    err = vspace_map_one_frame((void**)bootinfo, bootinfo_size, bootinfo_frame,
459                               NULL, NULL);
460    assert(err_is_ok(err));
461
462    return err;
463}
464
465static void multiboot_cap_reply(struct monitor_binding *st, struct capref cap,
466                                errval_t msgerr)
467{
468    errval_t err;
469    static cslot_t multiboot_slots = 0;
470
471    // All multiboot caps received
472    if (err_is_fail(msgerr)) {
473        // Request bootinfo frame
474        struct bootinfo *bi;
475        err = map_bootinfo(&bi);
476        assert(err_is_ok(err));
477
478        // Init ramfs
479        struct dirent *root = ramfs_init();
480
481        // Populate it with contents of multiboot
482        populate_multiboot(root, bi);
483
484        // Start the service
485        err = start_service(root);
486        assert(err_is_ok(err));
487        return;
488    }
489
490    // Move the cap into the multiboot cnode
491    struct capref dest = {
492        .cnode = cnode_module,
493        .slot  = multiboot_slots++,
494    };
495    err = cap_copy(dest, cap);
496    assert(err_is_ok(err));
497    err = cap_destroy(cap);
498    assert(err_is_ok(err));
499
500    err = st->tx_vtbl.multiboot_cap_request(st, NOP_CONT, multiboot_slots);
501    assert(err_is_ok(err));
502}
503
504static void bootstrap(void)
505{
506    errval_t err;
507
508    /* Create the module cnode */
509    struct capref modulecn_cap = {
510        .cnode = cnode_root,
511        .slot  = ROOTCN_SLOT_MODULECN,
512    };
513    err = cnode_create_raw(modulecn_cap, NULL, ObjType_L2CNode,
514                           ((cslot_t)1 << L2_CNODE_BITS), NULL);
515    if (err_is_fail(err)) {
516        DEBUG_ERR(err, "cnode_create_raw failed");
517        abort();
518    }
519
520    // XXX: Set reply handler
521    struct monitor_binding *st = get_monitor_binding();
522    st->rx_vtbl.multiboot_cap_reply = multiboot_cap_reply;
523
524    // Make first multiboot cap request
525    err = st->tx_vtbl.multiboot_cap_request(st, NOP_CONT, 0);
526    assert(err_is_ok(err));
527}
528
529int main(int argc, char *argv[])
530{
531    // Request multiboot caps and bootinfo from monitor
532    bootstrap();
533
534    messages_handler_loop();
535    return 0;
536}
537