| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/lib/bootfs/parser.h" |
| |
| #include <inttypes.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <zircon/boot/bootfs.h> |
| #include <zircon/errors.h> |
| #include <zircon/process.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| namespace bootfs { |
| |
| Parser::~Parser() { |
| if (dir_) { |
| uintptr_t addr = reinterpret_cast<uintptr_t>(dir_) - sizeof(zbi_bootfs_header_t); |
| zx::vmar::root_self()->unmap(addr, MappingSize()); |
| } |
| } |
| |
| zx_status_t Parser::Init(zx::unowned_vmo vmo) { |
| if (dir_ != nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| zx_status_t status = vmo->get_size(&vmo_size_); |
| if (status != ZX_OK) { |
| printf("Parser::Init: couldn't get bootfs VMO size - %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| |
| zbi_bootfs_header_t hdr; |
| status = vmo->read(&hdr, 0, sizeof(hdr)); |
| if (status != ZX_OK) { |
| printf("Parser::Init: couldn't read boot_data - %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| if (hdr.magic != ZBI_BOOTFS_MAGIC) { |
| printf("Parser::Init: incorrect bootdata header: %x\n", hdr.magic); |
| return ZX_ERR_IO; |
| } |
| zx_vaddr_t addr = 0; |
| status = |
| zx::vmar::root_self()->map(ZX_VM_PERM_READ, 0, *vmo, 0, sizeof(hdr) + hdr.dirsize, &addr); |
| if (status != ZX_OK) { |
| printf("Parser::Init: couldn't map directory: %d\n", status); |
| return status; |
| } |
| dirsize_ = hdr.dirsize; |
| dir_ = reinterpret_cast<char*>(addr) + sizeof(hdr); |
| return ZX_OK; |
| } |
| |
| zx_status_t Parser::Parse(Callback callback) { |
| if (dir_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| size_t avail = dirsize_; |
| auto* p = static_cast<char*>(dir_); |
| while (avail >= sizeof(zbi_bootfs_dirent_t)) { |
| auto e = reinterpret_cast<zbi_bootfs_dirent_t*>(p); |
| size_t dirent_size = ZBI_BOOTFS_DIRENT_SIZE(e->name_len); |
| if ((dirent_size > avail) || (e->name_len < 1) || (e->name_len > ZBI_BOOTFS_MAX_NAME_LEN)) { |
| printf("bootfs: invalid directory entry name length\n"); |
| return ZX_ERR_IO; |
| } |
| if ((e->name[0] == '/') || (e->name[e->name_len - 1] != 0)) { |
| printf("bootfs: invalid directory entry name\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((e->data_off % ZBI_BOOTFS_PAGE_SIZE) != 0) { |
| printf("bootfs: directory entry data offset not page-aligned\n"); |
| return ZX_ERR_IO; |
| } |
| if (e->data_off > vmo_size_ || e->data_len > (vmo_size_ - e->data_off)) { |
| printf("bootfs: directory entry data out-of-bounds\n"); |
| return ZX_ERR_IO; |
| } |
| |
| zx_status_t status = callback(e); |
| if (status != ZX_OK) { |
| return status; |
| } |
| p += dirent_size; |
| avail -= dirent_size; |
| } |
| if (avail > 0) { |
| printf("bootfs: partial directory entry header\n"); |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| } // namespace bootfs |