blob: 25c7ce0620d694151f33b9b6ffd0a59e1036db7b [file] [log] [blame]
// 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 <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fbl/unique_fd.h>
#include <fs-management/mount.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <lib/zx/process.h>
#include <loader-service/loader-service.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <utility>
#include "pkgfs-launcher.h"
namespace devmgr {
namespace {
void pkgfs_finish(FilesystemMounter* filesystems, zx::process proc, zx::channel pkgfs_root) {
auto deadline = zx::deadline_after(zx::sec(5));
zx_signals_t observed;
zx_status_t status =
proc.wait_one(ZX_USER_SIGNAL_0 | ZX_PROCESS_TERMINATED, deadline, &observed);
if (status != ZX_OK) {
printf("fshost: pkgfs did not signal completion: %d (%s)\n", status,
zx_status_get_string(status));
return;
}
if (!(observed & ZX_USER_SIGNAL_0)) {
printf("fshost: pkgfs terminated prematurely\n");
return;
}
// re-export /pkgfs/system as /system
zx::channel system_channel, system_req;
if (zx::channel::create(0, &system_channel, &system_req) != ZX_OK) {
return;
}
if (fdio_open_at(pkgfs_root.get(), "system", FS_READONLY_DIR_FLAGS,
system_req.release()) != ZX_OK) {
return;
}
// re-export /pkgfs/packages/shell-commands/0/bin as /bin
zx::channel bin_chan, bin_req;
if (zx::channel::create(0, &bin_chan, &bin_req) != ZX_OK) {
return;
}
if (fdio_open_at(pkgfs_root.get(), "packages/shell-commands/0/bin", FS_READONLY_DIR_FLAGS,
bin_req.release()) != ZX_OK) {
// non-fatal.
printf("fshost: failed to install /bin (could not open shell-commands)\n");
}
if (filesystems->InstallFs("/pkgfs", std::move(pkgfs_root)) != ZX_OK) {
printf("fshost: failed to install /pkgfs\n");
return;
}
if (filesystems->InstallFs("/system", std::move(system_channel)) != ZX_OK) {
printf("fshost: failed to install /system\n");
return;
}
// as above, failure of /bin export is non-fatal.
if (filesystems->InstallFs("/bin", std::move(bin_chan)) != ZX_OK) {
printf("fshost: failed to install /bin\n");
}
// start the appmgr
filesystems->FuchsiaStart();
}
// Launching pkgfs uses its own loader service and command lookup to run out of
// the blobfs without any real filesystem. Files are found by
// getenv("zircon.system.pkgfs.file.PATH") returning a blob content ID.
// That is, a manifest of name->blob is embedded in /boot/config/devmgr.
zx_status_t pkgfs_ldsvc_load_blob(void* ctx, const char* prefix, const char* name,
zx_handle_t* vmo) {
const int fs_blob_fd = static_cast<int>(reinterpret_cast<intptr_t>(ctx));
char key[256];
if (snprintf(key, sizeof(key), "zircon.system.pkgfs.file.%s%s", prefix, name) >=
(int)sizeof(key)) {
return ZX_ERR_BAD_PATH;
}
const char* blob = getenv(key);
if (blob == nullptr) {
return ZX_ERR_NOT_FOUND;
}
int fd = openat(fs_blob_fd, blob, O_RDONLY);
if (fd < 0) {
return ZX_ERR_NOT_FOUND;
}
zx::vmo nonexec_vmo;
zx::vmo exec_vmo;
zx_status_t status = fdio_get_vmo_clone(fd, nonexec_vmo.reset_and_get_address());
close(fd);
if (status != ZX_OK) {
return status;
}
status = nonexec_vmo.replace_as_executable(zx::handle(), &exec_vmo);
if (status != ZX_OK) {
return status;
}
status = zx_object_set_property(exec_vmo.get(), ZX_PROP_NAME, key, strlen(key));
if (status != ZX_OK) {
return status;
}
*vmo = exec_vmo.release();
return ZX_OK;
}
zx_status_t pkgfs_ldsvc_load_object(void* ctx, const char* name, zx_handle_t* vmo) {
return pkgfs_ldsvc_load_blob(ctx, "lib/", name, vmo);
}
zx_status_t pkgfs_ldsvc_load_abspath(void* ctx, const char* name, zx_handle_t* vmo) {
return pkgfs_ldsvc_load_blob(ctx, "", name + 1, vmo);
}
zx_status_t pkgfs_ldsvc_publish_data_sink(void* ctx, const char* name, zx_handle_t vmo) {
zx_handle_close(vmo);
return ZX_ERR_NOT_SUPPORTED;
}
void pkgfs_ldsvc_finalizer(void* ctx) {
close(static_cast<int>(reinterpret_cast<intptr_t>(ctx)));
}
const loader_service_ops_t pkgfs_ldsvc_ops = {
.load_object = pkgfs_ldsvc_load_object,
.load_abspath = pkgfs_ldsvc_load_abspath,
.publish_data_sink = pkgfs_ldsvc_publish_data_sink,
.finalizer = pkgfs_ldsvc_finalizer,
};
// Create a local loader service with a fixed mapping of names to blobs.
zx_status_t pkgfs_ldsvc_start(fbl::unique_fd fs_blob_fd, zx::channel* ldsvc) {
loader_service_t* service;
zx_status_t status =
loader_service_create(nullptr, &pkgfs_ldsvc_ops,
reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())),
&service);
if (status != ZX_OK) {
printf("fshost: cannot create pkgfs loader service: %d (%s)\n", status,
zx_status_get_string(status));
return status;
}
// The loader service now owns this FD
__UNUSED auto fd = fs_blob_fd.release();
status = loader_service_connect(service, ldsvc->reset_and_get_address());
loader_service_release(service);
if (status != ZX_OK) {
printf("fshost: cannot connect pkgfs loader service: %d (%s)\n", status,
zx_status_get_string(status));
}
return status;
}
bool pkgfs_launch(FilesystemMounter* filesystems) {
const char* cmd = getenv("zircon.system.pkgfs.cmd");
if (cmd == nullptr) {
return false;
}
fbl::unique_fd fs_blob_fd(open("/fs/blob", O_RDONLY | O_DIRECTORY));
if (!fs_blob_fd) {
printf("fshost: open(/fs/blob): %m\n");
return false;
}
zx::channel h0, h1;
zx_status_t status = zx::channel::create(0, &h0, &h1);
if (status != ZX_OK) {
printf("fshost: cannot create pkgfs root channel: %d (%s)\n", status,
zx_status_get_string(status));
return false;
}
auto args = ArgumentVector::FromCmdline(cmd);
auto argv = args.argv();
// Remove leading slashes before asking pkgfs_ldsvc_load_blob to load the
// file.
const char* file = argv[0];
while (file[0] == '/') {
++file;
}
zx::vmo executable;
status = pkgfs_ldsvc_load_blob(reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())),
"", argv[0], executable.reset_and_get_address());
if (status != ZX_OK) {
printf("fshost: cannot load pkgfs executable: %d (%s)\n", status,
zx_status_get_string(status));
return false;
}
zx::channel loader;
status = pkgfs_ldsvc_start(std::move(fs_blob_fd), &loader);
if (status != ZX_OK) {
printf("fshost: cannot pkgfs loader: %d (%s)\n", status, zx_status_get_string(status));
return false;
}
const zx_handle_t raw_h1 = h1.release();
zx::process proc;
args.Print("fshost");
status = devmgr_launch_with_loader(*zx::job::default_job(), "pkgfs",
std::move(executable), std::move(loader),
argv, nullptr, -1, &raw_h1,
(const uint32_t[]){PA_HND(PA_USER0, 0)}, 1, &proc,
FS_DATA | FS_BLOB | FS_SVC);
if (status != ZX_OK) {
printf("fshost: failed to launch %s: %d (%s)\n", cmd, status, zx_status_get_string(status));
return false;
}
pkgfs_finish(filesystems, std::move(proc), std::move(h0));
return true;
}
} // namespace
void LaunchBlobInit(FilesystemMounter* filesystems) {
pkgfs_launch(filesystems);
}
} // namespace devmgr