blob: d147021f470b35b81ca74863c17c89fde1b4f3ac [file] [log] [blame]
// Copyright 2021 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/storage/fshost/watcher.h"
#include <fcntl.h>
#include <lib/syslog/cpp/macros.h>
#include "src/storage/fshost/block-device-manager.h"
#include "src/storage/fshost/filesystem-mounter.h"
#include "src/storage/fshost/nand-device.h"
namespace fshost {
namespace {
namespace fio = fuchsia_io;
template <typename T>
zx_status_t AddDeviceImpl(BlockDeviceManager& manager, FilesystemMounter* mounter,
fbl::unique_fd fd) {
T device(mounter, std::move(fd), manager.config());
return manager.AddDevice(device);
}
} // namespace
std::vector<Watcher> Watcher::CreateWatchers() {
std::vector<Watcher> ret;
for (size_t i = 0; i < kWatcherTypeMax; i++) {
fbl::unique_fd dirfd(open(kWatcherPaths[i], O_DIRECTORY | O_RDONLY));
if (!dirfd) {
FX_LOGS(ERROR) << "failed to open " << kWatcherPaths[i] << ": " << strerror(errno);
continue;
}
fdio_cpp::FdioCaller caller(std::move(dirfd));
AddDeviceCallback callback;
switch (WatcherType(i)) {
case WatcherType::kWatcherTypeBlock:
callback = AddDeviceImpl<BlockDevice>;
break;
case WatcherType::kWatcherTypeNand:
callback = AddDeviceImpl<NandDevice>;
break;
default:
ZX_ASSERT_MSG(false, "Invalid watcher type %zu", i);
}
ret.emplace_back(Watcher(WatcherType(i), std::move(caller), std::move(callback)));
}
return ret;
}
zx_status_t Watcher::ReinitWatcher() {
watcher_.reset();
zx::channel watcher, server;
zx_status_t status = zx::channel::create(0, &watcher, &server);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create watcher channel: " << zx_status_get_string(status);
return status;
}
auto mask = fio::wire::kWatchMaskAll;
if (ignore_existing_) {
mask &= ~fio::wire::kWatchMaskExisting;
}
auto result =
fidl::WireCall<fio::Directory>(caller_.channel())->Watch(mask, 0, std::move(server));
if (!result.ok()) {
FX_LOGS(ERROR) << "failed to send watch: " << result.error();
return result.status();
}
if (result->s != ZX_OK) {
FX_LOGS(ERROR) << "watch failed: " << zx_status_get_string(result->s);
return result->s;
}
watcher_ = std::move(watcher);
return ZX_OK;
}
void Watcher::ProcessWatchMessages(cpp20::span<uint8_t> buf, WatcherCallback callback) {
uint8_t* iter = buf.begin();
while (iter + 2 <= buf.end()) {
uint8_t event = *iter++;
uint8_t name_len = *iter++;
if (iter + name_len > buf.end()) {
break;
}
// Save the first byte of the next message,
// and null-terminate the name.
uint8_t tmp = iter[name_len];
iter[name_len] = 0;
if (callback(*this, caller_.fd().get(), event, reinterpret_cast<const char*>(iter))) {
// We received a WATCH_EVENT_IDLE, and the watcher is paused.
// Bail out early.
ignore_existing_ = true;
return;
}
if (event == fio::wire::kWatchEventIdle) {
// WATCH_EVENT_IDLE - but the watcher is not paused. Finish processing this set of messages,
// but set ignore_existing_ to indicate that we should start checking for the pause signal.
ignore_existing_ = true;
break;
}
iter[name_len] = tmp;
iter += name_len;
}
}
zx_status_t Watcher::AddDevice(BlockDeviceManager& manager, FilesystemMounter* mounter,
fbl::unique_fd fd) {
return add_device_(manager, mounter, std::move(fd));
}
} // namespace fshost