blob: b72e5249651c476062f72112a5ac180b78d0210b [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 "virtual_file_provider/fuse_main.h"
#include <algorithm>
#include <string>
#include <utility>
#include <fuse/fuse.h>
#include <base/files/scoped_file.h>
#include <base/posix/eintr_wrapper.h>
#include <base/stl_util.h>
#include "virtual_file_provider/operation_throttle.h"
#include "virtual_file_provider/util.h"
namespace virtual_file_provider {
namespace {
constexpr char kFileSystemName[] = "virtual-file-provider";
// Maximum number of operations running at the same time.
constexpr int kMaxOperationCount = 1024;
struct FusePrivateData {
FuseMainDelegate* delegate;
OperationThrottle* operation_throttle;
};
FuseMainDelegate* GetDelegate() {
return static_cast<FusePrivateData*>(fuse_get_context()->private_data)
->delegate;
}
OperationThrottle* GetOperationThrottle() {
return static_cast<FusePrivateData*>(fuse_get_context()->private_data)
->operation_throttle;
}
int GetAttr(const char* path, struct stat* stat) {
// Everything except the root is a file.
if (path == std::string("/")) {
stat->st_mode = S_IFDIR;
stat->st_nlink = 2;
} else {
DCHECK_EQ('/', path[0]);
// File name is the ID.
std::string id(path + 1);
const int64_t size = GetDelegate()->GetSize(id);
if (size < 0) {
LOG(ERROR) << "Invalid ID " << id;
return -ENOENT;
}
stat->st_mode = S_IFREG;
stat->st_nlink = 1;
stat->st_size = size;
}
return 0;
}
int Open(const char* path, struct fuse_file_info* fi) {
return 0;
}
int Read(const char* path,
char* buf,
size_t size,
off_t off,
struct fuse_file_info* fi) {
auto operation_reference = GetOperationThrottle()->StartOperation();
DCHECK_EQ('/', path[0]);
// File name is the ID.
std::string id(path + 1);
// Adjust the size to avoid issuing unnecessary read requests.
const int64_t file_size = GetDelegate()->GetSize(id);
if (file_size < 0) {
LOG(ERROR) << "Invalid ID " << id;
return -EIO;
}
size = std::min(static_cast<int64_t>(size), file_size - off);
if (size <= 0) {
return 0;
}
// Create a pipe to receive data from chrome. By using pipe instead of D-Bus
// to receive data, we can reliably avoid deadlock at read(), provided chrome
// doesn't leak the file descriptor of the write end.
int fds[2] = {-1, -1};
if (pipe(fds) != 0) {
PLOG(ERROR) << "pipe() failed.";
return -EIO;
}
base::ScopedFD read_end(fds[0]), write_end(fds[1]);
// Send read request to chrome with the write end of the pipe.
GetDelegate()->HandleReadRequest(id, off, size, std::move(write_end));
// Read the data from the read end of the pipe.
size_t result = 0;
while (result < size) {
ssize_t r = HANDLE_EINTR(read(read_end.get(), buf + result, size - result));
if (r < 0) {
return -EIO;
}
if (r == 0) {
break;
}
result += r;
}
return result;
}
int Release(const char* path, struct fuse_file_info* fi) {
DCHECK_EQ('/', path[0]);
// File name is the ID.
std::string id(path + 1);
GetDelegate()->NotifyIdReleased(id);
return 0;
}
int ReadDir(const char* path,
void* buf,
fuse_fill_dir_t filler,
off_t offset,
struct fuse_file_info* fi) {
filler(buf, ".", nullptr, 0);
filler(buf, "..", nullptr, 0);
return 0;
}
void* Init(struct fuse_conn_info* conn) {
CHECK(ClearCapabilities());
// FUSE will overwrite the context's private_data with the return value.
// Just return the current private_data.
return fuse_get_context()->private_data;
}
} // namespace
int FuseMain(const base::FilePath& mount_path, FuseMainDelegate* delegate) {
const std::string path_str = mount_path.value();
const char* fuse_argv[] = {
kFileSystemName, path_str.c_str(),
"-f", // "-f" for foreground.
};
constexpr struct fuse_operations operations = {
.getattr = GetAttr,
.open = Open,
.read = Read,
.release = Release,
.readdir = ReadDir,
.init = Init,
};
OperationThrottle operation_throttle(kMaxOperationCount);
FusePrivateData private_data;
private_data.delegate = delegate;
private_data.operation_throttle = &operation_throttle;
return fuse_main(base::size(fuse_argv), const_cast<char**>(fuse_argv),
&operations, &private_data);
}
} // namespace virtual_file_provider