blob: 2de6d8ba6e77f49e43a4bd548f7d575def56b3a8 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_STORAGE_FSHOST_FS_MANAGER_H_
#define SRC_STORAGE_FSHOST_FS_MANAGER_H_
#include <fidl/fuchsia.process.lifecycle/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/wait.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/job.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <array>
#include <iterator>
#include <map>
#include "src/lib/storage/fs_management/cpp/format.h"
#include "src/lib/storage/vfs/cpp/vfs.h"
#include "src/storage/fshost/fdio.h"
#include "src/storage/fshost/fshost-boot-args.h"
#include "src/storage/fshost/fshost_config.h"
#include "src/storage/fshost/inspect-manager.h"
#include "src/storage/memfs/memfs.h"
#include "src/storage/memfs/vnode_dir.h"
namespace fshost {
class BlockWatcher;
// FsManager owns multiple sub-filesystems, managing them within a top-level
// in-memory filesystem.
class FsManager {
public:
explicit FsManager(std::shared_ptr<FshostBootArgs> boot_args);
~FsManager();
zx_status_t Initialize(fidl::ServerEnd<fuchsia_io::Directory> dir_request,
fidl::ServerEnd<fuchsia_process_lifecycle::Lifecycle> lifecycle_request,
const fshost_config::Config& config, BlockWatcher& watcher);
// MountPoint is a possible location that a filesystem can be installed at.
enum class MountPoint {
kData,
kFactory,
kDurable,
};
// Returns the fully qualified for the given mount point.
static const char* MountPointPath(MountPoint);
struct MountPointEndpoints {
fidl::UnownedClientEnd<fuchsia_io::Directory> export_root;
fidl::ServerEnd<fuchsia_io::Directory> server_end;
};
// Takes the server end of the specified mount point to send to a hosted filesystem. This channel
// pair will have been collecting queued requests since fshost was started.
//
// This can only be called once per mount point, any calls beyond that will return std::nullopt.
std::optional<MountPointEndpoints> TakeMountPointServerEnd(MountPoint point);
// Registers the device path for the given mount point.
void RegisterDevicePath(MountPoint point, std::string_view device_path);
// Creates a connection to the /fs dir in the outgoing directory.
zx::status<fidl::ClientEnd<fuchsia_io::Directory>> GetFsDir();
// Asynchronously shut down all the filesystems managed by fshost and then signal the main thread
// to exit. Calls |callback| when complete. The Shutdown process would block until
// ReadyForShutdown is called.
void Shutdown(fit::function<void(zx_status_t)> callback);
FshostInspectManager& inspect_manager() { return inspect_manager_; }
std::shared_ptr<FshostBootArgs> boot_args() { return boot_args_; }
bool IsShutdown();
void WaitForShutdown();
void ReadyForShutdown();
// Creates a new subdirectory in the fshost diagnostics directory by the name of
// |diagnostics_dir_name|, which forwards the diagnostics dir exposed in the export root directory
// of the given filesystem previously installed via |InstallFs()| at |point|.
zx_status_t ForwardFsDiagnosticsDirectory(MountPoint point,
std::string_view diagnostics_dir_name);
// Creates a new subdirectory in the fshost svc directory by the name of
// |service_name|, which forwards the service by the same name exposed in the outgoing service
// directory of the given filesystem previously installed via |InstallFs()| at |point|.
zx_status_t ForwardFsService(MountPoint point, const char* service_name);
// Disables reporting. Future calls to |FileReport| will be NOPs.
void DisableCrashReporting() { file_crash_report_ = false; }
// Note that additional reasons should be added sparingly, and only in cases where the data is
// useful and it would be difficult to debug the issue otherwise.
enum ReportReason {
// Unable to mount due to fsck failure.
kFsckFailure,
};
// Files a synthetic crash report. This is done in the background on a new thread, so never
// blocks. Note that there is no indication if the reporting fails.
void FileReport(fs_management::DiskFormat format, ReportReason reason) const;
zx_status_t AttachMount(std::string_view device_path,
fidl::ClientEnd<fuchsia_io::Directory> export_root,
std::string_view name);
zx_status_t DetachMount(std::string_view name);
zx::status<std::string> GetDevicePath(uint64_t fs_id);
// Returns the filesystem root for the given mount point.
zx::status<fidl::ClientEnd<fuchsia_io::Directory>> GetRoot(MountPoint point) const;
private:
class MountedFilesystem {
public:
struct Compare {
using is_transparent = void;
bool operator()(const std::unique_ptr<MountedFilesystem>& a,
const std::unique_ptr<MountedFilesystem>& b) const {
return a->name_ < b->name_;
}
bool operator()(const std::unique_ptr<MountedFilesystem>& a, std::string_view b) const {
return a->name_ < b;
}
bool operator()(std::string_view a, const std::unique_ptr<MountedFilesystem>& b) const {
return a < b->name_;
}
};
MountedFilesystem(std::string_view name, fidl::ClientEnd<fuchsia_io::Directory> export_root,
uint64_t fs_id)
: name_(name), export_root_(std::move(export_root)), fs_id_(fs_id) {}
~MountedFilesystem();
uint64_t fs_id() const { return fs_id_; }
private:
std::string name_;
fidl::ClientEnd<fuchsia_io::Directory> export_root_;
uint64_t fs_id_;
};
// Represents a channel pair for an expected filesystem instance. When fshost starts, it creates
// these channel pairs and exposes them in its outgoing directory. They queue filesystem
// requests, which are then serviced when the server_end is provided to the filesystem on
// startup.
//
// When a filesystem to be started, the server_end is taken with TakeMountPointServerEnd and
// replaced with std::nullopt. The server_end is then passed to the filesystem.
struct MountNode {
fidl::ClientEnd<fuchsia_io::Directory> export_root;
std::optional<fidl::ServerEnd<fuchsia_io::Directory>> server_end;
};
std::map<MountPoint, MountNode> mount_nodes_;
// The memfs which serves the /tmp directory.
std::unique_ptr<memfs::Memfs> tmp_;
std::unique_ptr<async::Loop> global_loop_;
fs::ManagedVfs vfs_;
// Serves inspect data.
FshostInspectManager inspect_manager_;
// Used to lookup configuration options stored in fuchsia.boot.Arguments
std::shared_ptr<fshost::FshostBootArgs> boot_args_;
fbl::RefPtr<fs::PseudoDir> svc_dir_;
fbl::RefPtr<fs::PseudoDir> fs_dir_;
fbl::RefPtr<fs::PseudoDir> mnt_dir_;
// The diagnostics directory for the fshost inspect tree.
// Each filesystem gets a subdirectory to host their own inspect tree.
// Archivist will parse all the inspect trees found in this directory tree.
fbl::RefPtr<fs::PseudoDir> diagnostics_dir_;
std::mutex shutdown_lock_;
bool shutdown_called_ __TA_GUARDED(shutdown_lock_) = false;
sync_completion_t shutdown_;
sync_completion_t ready_for_shutdown_;
bool file_crash_report_ = true;
std::set<std::unique_ptr<MountedFilesystem>, MountedFilesystem::Compare> mounted_filesystems_;
std::mutex device_paths_lock_;
std::unordered_map<uint64_t, std::string> device_paths_ __TA_GUARDED(device_paths_lock_);
};
} // namespace fshost
#endif // SRC_STORAGE_FSHOST_FS_MANAGER_H_