// Copyright 2017 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 "bootfs.h" #include "util.h" #pragma GCC visibility push(hidden) #include #include #include #include #pragma GCC visibility pop #define LOAD_OBJECT_FILE_PREFIX "lib/" struct loader_state { zx_handle_t log; struct bootfs* bootfs; char prefix[32]; size_t prefix_len; bool exclusive; }; static void loader_config(struct loader_state* state, const char* string, size_t len) { state->exclusive = false; if (string[len - 1] == '!') { --len; state->exclusive = true; } if (len >= sizeof(state->prefix) - 1) { fail(state->log, "loader-service config string too long"); } memcpy(state->prefix, string, len); state->prefix[len++] = '/'; state->prefix_len = len; } static zx_handle_t try_load_object(struct loader_state* state, const char* name, size_t len, size_t prefix_len) { char file[len + sizeof(LOAD_OBJECT_FILE_PREFIX) + prefix_len + 1]; memcpy(file, LOAD_OBJECT_FILE_PREFIX, sizeof(LOAD_OBJECT_FILE_PREFIX) - 1); memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1], state->prefix, prefix_len); memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len], name, len); file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len + len] = '\0'; return bootfs_open(state->log, "shared library", state->bootfs, file); } static zx_handle_t load_object(struct loader_state* state, const char* name, size_t len) { zx_handle_t vmo = try_load_object(state, name, len, state->prefix_len); if (vmo == ZX_HANDLE_INVALID && state->prefix_len > 0 && !state->exclusive) vmo = try_load_object(state, name, len, 0); if (vmo == ZX_HANDLE_INVALID) fail(state->log, "cannot find shared library '%s'", name); return vmo; } static bool handle_loader_rpc(struct loader_state* state, zx_handle_t channel) { ldmsg_req_t req; zx_handle_t reqhandle; uint32_t size; uint32_t hcount; zx_status_t status = zx_channel_read( channel, 0, &req, &reqhandle, sizeof(req), 1, &size, &hcount); // This is the normal error for the other end going away, // which happens when the process dies. if (status == ZX_ERR_PEER_CLOSED) { printl(state->log, "loader-service channel peer closed on read"); return false; } check(state->log, status, "zx_channel_read on loader-service channel failed"); const char* string; size_t string_len; status = ldmsg_req_decode(&req, size, &string, &string_len); if (status != ZX_OK) { fail(state->log, "loader-service request invalid"); } ldmsg_rsp_t rsp; memset(&rsp, 0, sizeof(rsp)); zx_handle_t handle = ZX_HANDLE_INVALID; switch (req.header.ordinal) { case LDMSG_OP_DONE: printl(state->log, "loader-service received DONE request"); goto no_reply; case LDMSG_OP_CONFIG: loader_config(state, string, string_len); break; case LDMSG_OP_LOAD_OBJECT: handle = load_object(state, string, string_len); break; case LDMSG_OP_CLONE: rsp.rv = ZX_ERR_NOT_SUPPORTED; goto error_reply; case LDMSG_OP_LOAD_SCRIPT_INTERPRETER: fail(state->log, "loader-service received LOAD_SCRIPT_INTERP request"); break; case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: { if (hcount != 1) { fail(state->log, "loader-service received DEBUG_PUBLISH_DATA_SINK request without VMO"); } char name[ZX_MAX_NAME_LEN]; status = zx_object_get_property(reqhandle, ZX_PROP_NAME, name, sizeof(name)); if (status != ZX_OK) { fail(state->log, "zx_object_get_property failed"); } uint64_t size; status = zx_vmo_get_size(reqhandle, &size); if (status != ZX_OK) { fail(state->log, "zx_vmo_get_size failed"); } printl(state->log, "loader-service data-sink \"%s\" DATA DROPPED: \"%s\", " "%zu bytes", string, name, (size_t)size); break; } default: fail(state->log, "loader-service received invalid opcode"); break; } rsp.rv = ZX_OK; rsp.object = handle == ZX_HANDLE_INVALID ? FIDL_HANDLE_ABSENT : FIDL_HANDLE_PRESENT; error_reply: rsp.header.txid = req.header.txid; rsp.header.ordinal = req.header.ordinal; // no opcodes which receive a handle are supported, but // we need to receive (and discard) the handle to politely // NAK clone requests if (hcount == 1) { zx_handle_close(reqhandle); } status = zx_channel_write(channel, 0, &rsp, ldmsg_rsp_get_size(&rsp), &handle, handle == ZX_HANDLE_INVALID ? 0 : 1); check(state->log, status, "zx_channel_write on loader-service channel failed"); return true; no_reply: if (hcount == 1) { zx_handle_close(reqhandle); } return false; } void loader_service(zx_handle_t log, struct bootfs* bootfs, zx_handle_t channel) { printl(log, "waiting for loader-service requests..."); struct loader_state state = { .log = log, .bootfs = bootfs, }; do { zx_signals_t signals; zx_status_t status = zx_object_wait_one( channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, ZX_TIME_INFINITE, &signals); if (status == ZX_ERR_BAD_STATE) { // This is the normal error for the other end going away, // which happens when the process dies. break; } check(log, status, "zx_object_wait_one failed on loader-service channel"); if (signals & ZX_CHANNEL_PEER_CLOSED) { printl(log, "loader-service channel peer closed"); break; } if (!(signals & ZX_CHANNEL_READABLE)) { fail(log, "unexpected signal state on loader-service channel"); } } while (handle_loader_rpc(&state, channel)); check(log, zx_handle_close(channel), "zx_handle_close failed on loader-service channel"); }