| // 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 "src/sys/appmgr/namespace_builder.h" |
| |
| #include <fcntl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/limits.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <zircon/processargs.h> |
| |
| #include <fbl/unique_fd.h> |
| #include <src/lib/fxl/strings/concatenate.h> |
| |
| #include "src/lib/files/directory.h" |
| #include "src/lib/files/path.h" |
| #include "src/lib/fsl/io/fd.h" |
| #include "src/sys/appmgr/allow_list.h" |
| |
| namespace component { |
| |
| namespace fio = ::llcpp::fuchsia::io; |
| |
| constexpr char kDeprecatedDataName[] = "deprecated-data"; |
| constexpr char kBlockedDataName[] = "data"; |
| |
| constexpr char kBuildInfoAllowList[] = "allowlist/build_info.txt"; |
| constexpr char kMiscStorageAllowList[] = "allowlist/deprecated_misc_storage.txt"; |
| |
| NamespaceBuilder::~NamespaceBuilder() = default; |
| |
| void NamespaceBuilder::AddFlatNamespace(fuchsia::sys::FlatNamespacePtr ns) { |
| if (ns && ns->paths.size() == ns->directories.size()) { |
| for (size_t i = 0; i < ns->paths.size(); ++i) { |
| AddDirectoryIfNotPresent(ns->paths.at(i), std::move(ns->directories.at(i))); |
| } |
| } |
| } |
| |
| void NamespaceBuilder::AddPackage(zx::channel package) { |
| PushDirectoryFromChannel("/pkg", std::move(package)); |
| } |
| |
| void NamespaceBuilder::AddConfigData(const SandboxMetadata& sandbox, const std::string& pkg_name) { |
| for (const auto& feature : sandbox.features()) { |
| if (feature == "config-data") { |
| PushDirectoryFromPathAs("/pkgfs/packages/config-data/0/meta/data/" + pkg_name, |
| "/config/data"); |
| } |
| } |
| } |
| |
| void NamespaceBuilder::AddDirectoryIfNotPresent(const std::string& path, zx::channel directory) { |
| if (std::find(paths_.begin(), paths_.end(), path) != paths_.end()) { |
| FX_LOGS(INFO) << "Namespace conflict for " << ns_id << ": " << path; |
| return; |
| } |
| PushDirectoryFromChannel(path, std::move(directory)); |
| } |
| |
| void NamespaceBuilder::AddServices(zx::channel services) { |
| PushDirectoryFromChannel("/svc", std::move(services)); |
| } |
| |
| void NamespaceBuilder::AddHub(const HubDirectoryFactory& hub_directory_factory) { |
| if (std::find(paths_.begin(), paths_.end(), "/hub") != paths_.end()) |
| return; |
| PushDirectoryFromChannel("/hub", hub_directory_factory()); |
| } |
| |
| zx_status_t NamespaceBuilder::AddSandbox(const SandboxMetadata& sandbox, |
| const HubDirectoryFactory& hub_directory_factory) { |
| return AddSandbox( |
| sandbox, hub_directory_factory, |
| [] { |
| FX_NOTREACHED() << "IsolatedDataPathFactory unexpectedly used"; |
| return fit::ok(""); |
| }, |
| [] { |
| FX_NOTREACHED() << "IsolatedCachePathFactory unexpectedly used"; |
| return fit::ok(""); |
| }, |
| [] { return fit::ok("/tmp"); }); |
| } |
| |
| zx_status_t NamespaceBuilder::AddSandbox( |
| const SandboxMetadata& sandbox, const HubDirectoryFactory& hub_directory_factory, |
| const IsolatedDataPathFactory& isolated_data_path_factory, |
| const IsolatedCachePathFactory& isolated_cache_path_factory, |
| const IsolatedTempPathFactory& isolated_temp_path_factory) { |
| for (const auto& path : sandbox.dev()) { |
| if (path == "class") { |
| FX_LOGS(WARNING) << "Ignoring request for all device classes"; |
| continue; |
| } |
| PushDirectoryFromPath("/dev/" + path); |
| } |
| |
| for (const auto& path : sandbox.system()) { |
| // 'deprecated-data' is the value used to access /system/data |
| // to request a directory inside /system/data 'deprecated-data/some/path' is supplied |
| if (path == kDeprecatedDataName || |
| path.find(fxl::Concatenate({kDeprecatedDataName, "/"})) == 0) { |
| FX_LOGS(ERROR) << "Request for 'deprecated-data' by " << ns_id |
| << " ignored, this feature is no longer available"; |
| } else if (path == kBlockedDataName || |
| path.find(fxl::Concatenate({kBlockedDataName, "/"})) == 0) { |
| FX_LOGS(ERROR) << "Request for 'data' in namespace '" << ns_id |
| << "' ignored, this feature is no longer available"; |
| } else { |
| PushDirectoryFromPath("/system/" + path); |
| } |
| } |
| |
| for (const auto& path : sandbox.pkgfs()) |
| PushDirectoryFromPath("/pkgfs/" + path); |
| |
| // Prioritize isolated persistent storage over shell feature, if both are |
| // present. |
| if (sandbox.HasFeature("isolated-persistent-storage")) { |
| auto data_path = isolated_data_path_factory(); |
| if (data_path.is_ok()) { |
| PushDirectoryFromPathAs(data_path.value(), "/data"); |
| } else { |
| return data_path.error(); |
| } |
| } |
| |
| for (const auto& feature : sandbox.features()) { |
| if (feature == "root-ssl-certificates") { |
| PushDirectoryFromPathAs("/pkgfs/packages/root_ssl_certificates/0/data", "/config/ssl"); |
| } else if (feature == "deprecated-shell") { |
| PushDirectoryFromPathAs("/pkgfs/packages/root_ssl_certificates/0/data", "/config/ssl"); |
| PushDirectoryFromPathAs("/pkgfs/packages/shell-commands/0/bin", "/bin"); |
| PushDirectoryFromPath("/blob"); |
| PushDirectoryFromPath("/boot"); |
| // Note that if the 'isolated-persistent-storage' feature is present, |
| // this is a no-op since it has handled first above. |
| // PushDirectoryFromPath does not override existing directories. |
| PushDirectoryFromPath("/data"); |
| PushDirectoryFromPath("/dev"); |
| AddHub(hub_directory_factory); |
| PushDirectoryFromPath("/pkgfs"); |
| PushDirectoryFromPath("/system"); |
| PushDirectoryFromPath("/tmp"); |
| PushDirectoryFromPath("/hub-v2"); |
| } else if (feature == "shell-commands") { |
| PushDirectoryFromPathAs("/pkgfs/packages/shell-commands/0/bin", "/bin"); |
| } else if (feature == "vulkan") { |
| PushDirectoryFromPath("/dev/class/goldfish-address-space"); |
| PushDirectoryFromPath("/dev/class/goldfish-control"); |
| PushDirectoryFromPath("/dev/class/goldfish-pipe"); |
| PushDirectoryFromPath("/dev/class/gpu"); |
| PushDirectoryFromPathAs("/pkgfs/packages/config-data/0/meta/data/vulkan-icd/icd.d", |
| "/config/vulkan/icd.d"); |
| } else if (feature == "isolated-cache-storage") { |
| PushDirectoryFromPathAs(isolated_cache_path_factory().value(), "/cache"); |
| } else if (feature == "isolated-temp") { |
| PushDirectoryFromPathAs(isolated_temp_path_factory().value(), "/tmp"); |
| } else if (feature == "hub") { |
| AddHub(hub_directory_factory); |
| } else if (feature == "factory-data") { |
| PushDirectoryFromPath("/factory"); |
| } else if (feature == "durable-data") { |
| PushDirectoryFromPath("/durable"); |
| // Begin allowlisted namespace features |
| } else if (feature == "build-info") { |
| // fxbug.dev/50308 |
| AllowList build_info_allowlist(appmgr_config_dir_, kBuildInfoAllowList); |
| FuchsiaPkgUrl pkg_url; |
| if (pkg_url.Parse(ns_id) && build_info_allowlist.IsAllowed(pkg_url)) { |
| PushDirectoryFromPathAs("/pkgfs/packages/build-info/0/data", "/config/build-info"); |
| } else { |
| FX_LOGS(WARNING) << "Component " << ns_id |
| << " is not allowlisted to use build-info. See fxbug.dev/50308."; |
| } |
| } else if (feature == "deprecated-misc-storage") { |
| AllowList misc_storage_allowlist(appmgr_config_dir_, kMiscStorageAllowList); |
| FuchsiaPkgUrl pkg_url; |
| if (pkg_url.Parse(ns_id) && misc_storage_allowlist.IsAllowed(pkg_url)) { |
| const std::string data_dir = "/data/misc"; |
| if (files::CreateDirectory(data_dir)) { |
| PushDirectoryFromPathAs(data_dir, "/misc"); |
| } else { |
| FX_LOGS(ERROR) << "Failed to create deprecated-misc-storage directory"; |
| } |
| } else { |
| FX_LOGS(ERROR) << "Component " << ns_id |
| << " is not allowed to use deprecated-misc-storage. Blocked by allowlist."; |
| } |
| } |
| } |
| |
| for (const auto& path : sandbox.boot()) |
| PushDirectoryFromPath("/boot/" + path); |
| |
| return ZX_OK; |
| } |
| |
| void NamespaceBuilder::PushDirectoryFromPath(std::string path) { |
| PushDirectoryFromPathAs(path, path); |
| } |
| |
| void NamespaceBuilder::PushDirectoryFromPathAs(std::string src_path, std::string dst_path) { |
| PushDirectoryFromPathAsWithPermissions( |
| std::move(src_path), std::move(dst_path), |
| fio::OPEN_FLAG_DIRECTORY | fio::OPEN_FLAG_POSIX | fio::OPEN_RIGHT_READABLE); |
| } |
| |
| void NamespaceBuilder::PushDirectoryFromPathAsWithPermissions(std::string src_path, |
| std::string dst_path, |
| uint64_t flags) { |
| if (std::find(paths_.begin(), paths_.end(), dst_path) != paths_.end()) { |
| return; |
| } |
| fbl::unique_fd dir; |
| zx_status_t status = fdio_open_fd(src_path.c_str(), flags, dir.reset_and_get_address()); |
| if (status != ZX_OK) { |
| return; |
| } |
| zx::channel handle = fsl::CloneChannelFromFileDescriptor(dir.get()); |
| if (!handle) { |
| FX_DLOGS(WARNING) << "Failed to clone channel for " << src_path; |
| return; |
| } |
| PushDirectoryFromChannel(std::move(dst_path), std::move(handle)); |
| } |
| |
| void NamespaceBuilder::PushDirectoryFromChannel(std::string path, zx::channel channel) { |
| FX_DCHECK(std::find(paths_.begin(), paths_.end(), path) == paths_.end()); |
| types_.push_back(PA_HND(PA_NS_DIR, types_.size())); |
| handles_.push_back(channel.get()); |
| paths_.push_back(std::move(path)); |
| |
| handle_pool_.push_back(std::move(channel)); |
| } |
| |
| fdio_flat_namespace_t* NamespaceBuilder::Build() { |
| path_data_.resize(paths_.size()); |
| for (size_t i = 0; i < paths_.size(); ++i) |
| path_data_[i] = paths_[i].c_str(); |
| |
| flat_ns_.count = types_.size(); |
| flat_ns_.handle = handles_.data(); |
| flat_ns_.type = types_.data(); |
| flat_ns_.path = path_data_.data(); |
| Release(); |
| return &flat_ns_; |
| } |
| |
| fuchsia::sys::FlatNamespace NamespaceBuilder::BuildForRunner() { |
| fuchsia::sys::FlatNamespace flat_namespace; |
| flat_namespace.paths.clear(); |
| flat_namespace.directories.clear(); |
| |
| for (auto& path : paths_) { |
| flat_namespace.paths.push_back(std::move(path)); |
| } |
| |
| for (auto& handle : handle_pool_) { |
| flat_namespace.directories.push_back(std::move(handle)); |
| } |
| return flat_namespace; |
| } |
| |
| void NamespaceBuilder::Release() { |
| for (auto& handle : handle_pool_) |
| (void)handle.release(); |
| handle_pool_.clear(); |
| } |
| |
| } // namespace component |