blob: 17b7e61d5cf0fc6e71cccd0e11b32f4b92fa8bb2 [file] [log] [blame]
// Copyright 2017 The Chromium OS 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 <sys/vfs.h>
#include <memory>
#include <base/at_exit.h>
#include <base/bind.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/message_loop/message_loop.h>
#include <base/posix/eintr_wrapper.h>
#include <base/threading/platform_thread.h>
#include <base/threading/thread.h>
#include <brillo/syslog_logging.h>
#include "virtual_file_provider/fuse_main.h"
#include "virtual_file_provider/service.h"
#include "virtual_file_provider/util.h"
namespace virtual_file_provider {
namespace {
// Service thread handles D-Bus method calls.
class ServiceThread : public base::Thread {
public:
ServiceThread(const base::FilePath& fuse_mount_path, SizeMap* size_map)
: Thread("Service thread"),
fuse_mount_path_(fuse_mount_path),
service_(std::make_unique<Service>(fuse_mount_path, size_map)) {}
~ServiceThread() override { Stop(); }
Service* service() { return service_.get(); }
protected:
// base::Thread overrides:
void Init() override {
CHECK(WaitForFuseMount());
CHECK(ClearCapabilities());
CHECK(service_->Initialize());
}
void CleanUp() override {
service_.reset(); // Must be destructed on the same thread as Initialize().
}
private:
// Waits for the FUSE mount to get ready.
bool WaitForFuseMount() {
constexpr int kMaxRetryCount = 3000;
constexpr base::TimeDelta kRetryInterval =
base::TimeDelta::FromMilliseconds(1);
for (int retry_count = 0; retry_count < kMaxRetryCount; ++retry_count) {
struct statfs buf = {};
if (HANDLE_EINTR(statfs(fuse_mount_path_.value().c_str(), &buf)) < 0) {
PLOG(ERROR) << "statfs() failed.";
return false;
}
// Compare with FUSE_SUPER_MAGIC (defined in <kernel>/fs/fuse/inode.c).
if (buf.f_type == 0x65735546)
return true;
base::PlatformThread::Sleep(kRetryInterval);
}
LOG(ERROR) << "Timed out while waiting for FUSE mount.";
return false;
}
const base::FilePath fuse_mount_path_;
std::unique_ptr<Service> service_;
DISALLOW_COPY_AND_ASSIGN(ServiceThread);
};
class FuseMainDelegateImpl : public FuseMainDelegate {
public:
FuseMainDelegateImpl(ServiceThread* service_thread, SizeMap* size_map)
: service_thread_(service_thread), size_map_(size_map) {}
~FuseMainDelegateImpl() override = default;
// FuseMainDelegate overrides:
int64_t GetSize(const std::string& id) override;
void HandleReadRequest(const std::string& id,
int64_t offset,
int64_t size,
base::ScopedFD fd) override;
void NotifyIdReleased(const std::string& id) override;
private:
ServiceThread* const service_thread_;
SizeMap* const size_map_;
DISALLOW_COPY_AND_ASSIGN(FuseMainDelegateImpl);
};
int64_t FuseMainDelegateImpl::GetSize(const std::string& id) {
return size_map_->GetSize(id);
}
void FuseMainDelegateImpl::HandleReadRequest(const std::string& id,
int64_t offset,
int64_t size,
base::ScopedFD fd) {
service_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&Service::SendReadRequest,
// This is safe as service_thread outlives the FUSE main loop.
base::Unretained(service_thread_->service()),
id, offset, size, base::Passed(&fd)));
}
void FuseMainDelegateImpl::NotifyIdReleased(const std::string& id) {
if (!size_map_->Erase(id)) {
LOG(ERROR) << "Invalid ID " << id;
return;
}
service_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&Service::SendIdReleased,
// This is safe as service_thread outlives the FUSE main loop.
base::Unretained(service_thread_->service()), id));
}
} // namespace
} // namespace virtual_file_provider
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <FUSE mount path>\n", argv[0]);
return 1;
}
base::FilePath fuse_mount_path(argv[1]);
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr);
base::AtExitManager at_exit_manager;
virtual_file_provider::SizeMap size_map;
// Run D-Bus service on the service thread.
virtual_file_provider::ServiceThread service_thread(
fuse_mount_path, &size_map);
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
service_thread.StartWithOptions(options);
// Enter the FUSE main loop.
virtual_file_provider::FuseMainDelegateImpl delegate(&service_thread,
&size_map);
return virtual_file_provider::FuseMain(fuse_mount_path, &delegate);
}