1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <inttypes.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10#include <zircon/boot/bootdata.h>
11#include <zircon/process.h>
12#include <zircon/syscalls.h>
13#include <zircon/types.h>
14
15#include <fbl/algorithm.h>
16
17#include "bootfs.h"
18
19Bootfs::~Bootfs() = default;
20
21zx_status_t Bootfs::Create(zx::vmo vmo, Bootfs* bfs_out) {
22    bootfs_header_t hdr;
23    zx_status_t r = vmo.read(&hdr, 0, sizeof(hdr));
24    if (r < 0) {
25        printf("bootfs_create: couldn't read boot_data - %d\n", r);
26        return r;
27    }
28    if (hdr.magic != BOOTFS_MAGIC) {
29        printf("bootfs_create: incorrect bootdata header: %x\n", hdr.magic);
30        return ZX_ERR_IO;
31    }
32    zx_vaddr_t addr;
33    if ((r = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo.get(), 0,
34                         sizeof(hdr) + hdr.dirsize,
35                         &addr)) < 0) {
36        printf("boofts_create: couldn't map directory: %d\n", r);
37        return r;
38    }
39    auto dir = reinterpret_cast<char*>(addr) + sizeof(hdr);
40    *bfs_out = Bootfs(fbl::move(vmo), hdr.dirsize, dir);
41    return ZX_OK;
42}
43
44void Bootfs::Destroy() {
45    vmo_.reset();
46    zx_vmar_unmap(zx_vmar_root_self(),
47                  (uintptr_t)dir_ - sizeof(bootfs_header_t),
48                  MappingSize());
49}
50
51zx_status_t Bootfs::Parse(Callback callback, void* cookie) {
52    size_t avail = dirsize_;
53    auto* p = static_cast<char*>(dir_);
54    zx_status_t r;
55    while (avail > sizeof(bootfs_entry_t)) {
56        auto e = reinterpret_cast<bootfs_entry_t*>(p);
57        size_t sz = BOOTFS_RECSIZE(e);
58        if ((e->name_len < 1) || (e->name_len > BOOTFS_MAX_NAME_LEN) ||
59            (e->name[e->name_len - 1] != 0) || (sz > avail)) {
60            printf("bootfs: bogus entry!\n");
61            return ZX_ERR_IO;
62        }
63        if ((r = callback(cookie, e)) != ZX_OK) {
64            return r;
65        }
66        p += sz;
67        avail -= sz;
68    }
69    return ZX_OK;
70}
71
72zx_status_t Bootfs::Open(const char* name, zx::vmo* vmo_out, uint32_t* size_out) {
73    size_t name_len = strlen(name) + 1;
74    size_t avail = dirsize_;
75    auto p = static_cast<char*>(dir_);
76    bootfs_entry_t* e;
77    while (avail > sizeof(bootfs_entry_t)) {
78        e = reinterpret_cast<bootfs_entry_t*>(p);
79        size_t sz = BOOTFS_RECSIZE(e);
80        if ((e->name_len < 1) || (e->name_len > BOOTFS_MAX_NAME_LEN) ||
81            (e->name[e->name_len - 1] != 0) || (sz > avail)) {
82            printf("bootfs: bogus entry!\n");
83            return ZX_ERR_IO;
84        }
85        if ((name_len == e->name_len) && (memcmp(name, e->name, name_len) == 0)) {
86            goto found;
87        }
88        p += sz;
89        avail -= sz;
90    }
91    printf("bootfs_open: '%s' not found\n", name);
92    return ZX_ERR_NOT_FOUND;
93
94found:;
95    zx::vmo vmo;
96    zx_status_t r;
97
98    // Clone a private copy of the file's subset of the bootfs VMO.
99    // TODO(mcgrathr): Create a plain read-only clone when the feature
100    // is implemented in the VM.
101    if ((r = vmo_.clone(ZX_VMO_CLONE_COPY_ON_WRITE,
102                        e->data_off, e->data_len, &vmo)) != ZX_OK) {
103        return r;
104    }
105
106    vmo.set_property(ZX_PROP_NAME, name, name_len - 1);
107
108    // Drop unnecessary ZX_RIGHT_WRITE rights.
109    // TODO(mcgrathr): Should be superfluous with read-only zx_vmo_clone.
110    if ((r = vmo.replace(ZX_RIGHTS_BASIC | ZX_RIGHT_READ |
111                         ZX_RIGHT_MAP | ZX_RIGHT_GET_PROPERTY,
112                         &vmo)) != ZX_OK) {
113        return r;
114    }
115
116    // TODO(mdempsky): Restrict to bin/ and lib/.
117    if ((vmo.replace_as_executable(zx::handle(), &vmo)) != ZX_OK) {
118        return r;
119    }
120
121    *vmo_out = fbl::move(vmo);
122    if (size_out) {
123        *size_out = e->data_len;
124    }
125    return ZX_OK;
126}
127
128zx::vmo Bootfs::DuplicateVmo() {
129    zx::vmo duplicate;
130    vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate);
131    return duplicate;
132}
133