blob: 362f66a5b3ec2a5c703a203da38036ab71ed332c [file] [log] [blame]
// Copyright 2021 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 "fusebox/fuse_path_inodes.h"
#include <vector>
#include <base/check.h>
#include <base/check_op.h>
#include <base/containers/contains.h>
#include <base/logging.h>
#include <base/strings/string_piece.h>
namespace {
std::string GetParentChildName(const char* path) {
base::StringPiece name(path ? path : ".");
if (name.empty() || name == "/")
return path;
if (name == "." || name == "..")
return {};
if (name.find('/') != base::StringPiece::npos)
return {};
return std::string("/").append(name.data(), name.size());
}
using Node = fusebox::Node;
inline Node* NodeError(int error) {
errno = error;
return nullptr;
}
Node* CreateNode(ino_t parent, const std::string& child, ino_t ino) {
Node* node = new Node();
CHECK(node);
node->device = 0;
node->parent = parent;
node->ino = ino;
node->name = child;
node->refcount = 1;
return node;
}
} // namespace
namespace fusebox {
InodeTable::InodeTable() : ino_(0), stat_cache_(1024) {
root_node_ = InsertNode(CreateNode(0, "/", CreateIno()));
}
ino_t InodeTable::CreateIno() {
ino_t ino = ++ino_;
CHECK(ino) << "inodes wrapped";
return ino;
}
Node* InodeTable::Create(ino_t parent, const char* name) {
std::string child = GetParentChildName(name);
if (child.empty() || !parent)
return NodeError(EINVAL);
auto p = parent_map_.find(std::to_string(parent).append(child));
if (p != parent_map_.end())
return NodeError(EEXIST);
return InsertNode(CreateNode(parent, child, CreateIno()));
}
Node* InodeTable::Lookup(ino_t ino, uint64_t ref) {
auto n = node_map_.find(ino);
if (n == node_map_.end())
return NodeError(ENOENT);
Node* node = n->second.get();
node->refcount += ref;
return node;
}
Node* InodeTable::Lookup(ino_t parent, const char* name, uint64_t ref) {
std::string child = GetParentChildName(name);
if (child.empty())
return NodeError(EINVAL);
auto p = parent_map_.find(std::to_string(parent).append(child));
if (p == parent_map_.end())
return NodeError(ENOENT);
Node* node = p->second;
node->refcount += ref;
return node;
}
Node* InodeTable::Move(Node* node, ino_t parent, const char* name) {
CHECK_NE(node, root_node_);
if (node_map_.find(parent) == node_map_.end())
return NodeError(EINVAL);
std::string child = GetParentChildName(name);
if (child.empty() || !node || node->ino == parent)
return NodeError(EINVAL);
auto p = parent_map_.find(std::to_string(parent).append(child));
if (p != parent_map_.end())
return NodeError(EEXIST);
RemoveNode(node);
node->parent = parent;
node->name = child;
return InsertNode(node);
}
bool InodeTable::Forget(ino_t ino, uint64_t nlookup) {
if (!ino || ino == root_node_->ino)
return false; // Ignore root node.
Node* node = Lookup(ino);
if (!node)
return true;
if (nlookup < node->refcount) {
node->refcount -= nlookup;
return false;
}
delete RemoveNode(node);
ForgetStat(ino);
return true;
}
std::string InodeTable::GetName(ino_t ino) {
if (Node* node = Lookup(ino))
return node->name;
return {};
}
std::string InodeTable::GetPath(Node* node) {
DCHECK(node);
std::vector<std::string> names;
while (node->ino && node->parent) {
names.push_back(node->name);
auto parent = node_map_.find(node->parent);
CHECK(parent != node_map_.end());
node = parent->second.get();
}
std::string path;
for (auto it = names.rbegin(); it != names.rend(); ++it)
path.append(it->data(), it->size());
if (path.empty())
path.push_back('/');
return path;
}
void InodeTable::SetStat(ino_t ino, struct stat stat, double timeout) {
DCHECK(ino);
stat.st_ino = ino;
struct Stat item;
item.time = timeout ? std::time(nullptr) + time_t(timeout) : time_t(0);
item.stat = stat;
stat_cache_.Put(stat.st_ino, item);
}
bool InodeTable::GetStat(ino_t ino, struct stat* stat) {
DCHECK(stat);
auto it = stat_cache_.Get(ino);
if (it == stat_cache_.end())
return false;
const auto& item = it->second;
if (item.time && item.time < std::time(nullptr)) {
stat_cache_.Erase(it); // stat time out
return false;
}
*stat = item.stat;
return true;
}
void InodeTable::ForgetStat(ino_t ino) {
auto it = stat_cache_.Peek(ino);
if (it != stat_cache_.end())
stat_cache_.Erase(it);
}
Node* InodeTable::InsertNode(Node* node) {
DCHECK(node);
DCHECK(node->ino);
CHECK_NE(node->parent, node->ino);
parent_map_[std::to_string(node->parent).append(node->name)] = node;
CHECK(!base::Contains(node_map_, node->ino));
node_map_[node->ino].reset(node);
return node;
}
Node* InodeTable::RemoveNode(Node* node) {
DCHECK(node);
parent_map_.erase(std::to_string(node->parent).append(node->name));
auto n = node_map_.find(node->ino);
CHECK(n != node_map_.end());
n->second.release();
node_map_.erase(n);
return node;
}
} // namespace fusebox