fuseFhMap: translate FUSE file handles to FileNode pointers

Previously, we used raw pointers to the FileNode as FUSE file handles.

With this change, we instead pass an arbitrary value (a uint64 counter)
to the kernel as FUSE file handle, and use the "fuseFhMap" look-up table
to convert back to the FileNode pointer.

This gets rid of a lot of scary void pointer casts. The performance
cost is not measurable.
This commit is contained in:
Jakob Unterwurzacher 2017-07-22 19:19:21 +02:00
parent 9a4ea2007f
commit e2f0f8e3c6
6 changed files with 62 additions and 19 deletions

View File

@ -34,6 +34,7 @@ EncFS_Context::EncFS_Context() {
pthread_mutex_init(&contextMutex, 0);
usageCount = 0;
currentFuseFh = 1;
}
EncFS_Context::~EncFS_Context() {
@ -106,17 +107,20 @@ void EncFS_Context::renameNode(const char *from, const char *to) {
}
}
FileNode *EncFS_Context::putNode(const char *path,
std::shared_ptr<FileNode> &&node) {
// putNode stores "node" under key "path" in the "openFiles" map. It
// increments the reference count if the key already exists.
void EncFS_Context::putNode(const char *path,
std::shared_ptr<FileNode> node) {
Lock lock(contextMutex);
auto &list = openFiles[std::string(path)];
list.push_front(std::move(node));
return list.front().get();
// The length of "list" serves as the reference count.
list.push_front(node);
fuseFhMap[node->fuseFh] = node;
}
// eraseNode is called by encfs_release in response to the RELEASE
// FUSE-command we get from the kernel.
void EncFS_Context::eraseNode(const char *path, FileNode *pl) {
void EncFS_Context::eraseNode(const char *path, std::shared_ptr<FileNode> fnode) {
Lock lock(contextMutex);
FileMap::iterator it = openFiles.find(std::string(path));
@ -130,7 +134,25 @@ void EncFS_Context::eraseNode(const char *path, FileNode *pl) {
if (it->second.empty()) {
fn->canary = CANARY_RELEASED;
openFiles.erase(it);
fuseFhMap.erase(fn->fuseFh);
}
}
// nextFuseFh returns the next unused uint64 to serve as the FUSE file
// handle for the kernel.
uint64_t EncFS_Context::nextFuseFh(void) {
// This is thread-safe because currentFuseFh is declared as std::atomic
return currentFuseFh++;
}
// lookupFuseFh finds "n" in "fuseFhMap" and returns the FileNode.
std::shared_ptr<FileNode> EncFS_Context::lookupFuseFh(uint64_t n) {
Lock lock(contextMutex);
auto it = fuseFhMap.find(n);
if (it == fuseFhMap.end()) {
return nullptr;
}
return it->second;
}
} // namespace encfs

View File

@ -27,6 +27,7 @@
#include <set>
#include <string>
#include <unordered_map>
#include <atomic>
#include "encfs.h"
@ -46,9 +47,9 @@ class EncFS_Context {
void getAndResetUsageCounter(int *usage, int *openCount);
FileNode *putNode(const char *path, std::shared_ptr<FileNode> &&node);
void putNode(const char *path, std::shared_ptr<FileNode> node);
void eraseNode(const char *path, FileNode *fnode);
void eraseNode(const char *path, std::shared_ptr<FileNode> fnode);
void renameNode(const char *oldName, const char *newName);
@ -69,6 +70,9 @@ class EncFS_Context {
pthread_cond_t wakeupCond;
pthread_mutex_t wakeupMutex;
uint64_t nextFuseFh();
std::shared_ptr<FileNode> lookupFuseFh(uint64_t);
private:
/* This placeholder is what is referenced in FUSE context (passed to
* callbacks).
@ -89,6 +93,9 @@ class EncFS_Context {
int usageCount;
std::shared_ptr<DirNode> root;
std::atomic<std::uint64_t> currentFuseFh;
std::unordered_map<uint64_t, std::shared_ptr<FileNode>> fuseFhMap;
};
int remountFS(EncFS_Context *ctx);

View File

@ -644,8 +644,9 @@ std::shared_ptr<FileNode> DirNode::findOrCreate(const char *plainName) {
if (!node) {
uint64_t iv = 0;
string cipherName = naming->encodePath(plainName, &iv);
uint64_t fuseFh = ctx->nextFuseFh();
node.reset(new FileNode(this, fsConfig, plainName,
(rootDir + cipherName).c_str()));
(rootDir + cipherName).c_str(), fuseFh));
if (fsConfig->config->externalIVChaining) node->setName(0, 0, iv);

View File

@ -53,7 +53,8 @@ namespace encfs {
*/
FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,
const char *plaintextName_, const char *cipherName_) {
const char *plaintextName_, const char *cipherName_, uint64_t fuseFh) {
pthread_mutex_init(&mutex, 0);
Lock _lock(mutex);
@ -66,6 +67,8 @@ FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,
this->fsConfig = cfg;
this->fuseFh = fuseFh;
// chain RawFileIO & CipherFileIO
std::shared_ptr<FileIO> rawIO(new RawFileIO(_cname));
io = std::shared_ptr<FileIO>(new CipherFileIO(rawIO, fsConfig));

View File

@ -47,13 +47,16 @@ class FileIO;
class FileNode {
public:
FileNode(DirNode *parent, const FSConfigPtr &cfg, const char *plaintextName,
const char *cipherName);
const char *cipherName, uint64_t fuseFh);
~FileNode();
// Use an atomic type. The canary is accessed without holding any
// locks.
std::atomic<std::uint32_t> canary;
// FUSE file handle that is passed to the kernel
uint64_t fuseFh;
const char *plaintextName() const;
const char *cipherName() const;

View File

@ -142,7 +142,7 @@ static int withFileNode(const char *opName, const char *path,
try {
auto do_op = [&FSRoot, opName, &op](FileNode *fnode) {
auto do_op = [&FSRoot, opName, &op](std::shared_ptr<FileNode> fnode) {
rAssert(fnode != nullptr);
checkCanary(fnode);
VLOG(1) << "op: " << opName << " : " << fnode->cipherName();
@ -153,13 +153,19 @@ static int withFileNode(const char *opName, const char *path,
<< fnode->cipherName() << "'";
return -EIO;
}
return op(fnode);
return op(fnode.get());
};
if (fi != nullptr && fi->fh != 0)
res = do_op(reinterpret_cast<FileNode *>(fi->fh));
else
res = do_op(FSRoot->lookupNode(path, opName).get());
if (fi != nullptr && fi->fh != 0) {
auto node = ctx->lookupFuseFh(fi->fh);
if (node == nullptr) {
auto msg = "fh=" + std::to_string(fi->fh) + " not found in fuseFhMap";
throw Error(msg.c_str());
}
res = do_op(node);
} else {
res = do_op(FSRoot->lookupNode(path, opName));
}
if (res < 0) {
RLOG(DEBUG) << "op: " << opName << " error: " << strerror(-res);
@ -555,8 +561,8 @@ int encfs_open(const char *path, struct fuse_file_info *file) {
<< file->flags;
if (res >= 0) {
file->fh =
reinterpret_cast<uintptr_t>(ctx->putNode(path, std::move(fnode)));
ctx->putNode(path, fnode);
file->fh = fnode->fuseFh;
res = ESUCCESS;
}
}
@ -611,7 +617,8 @@ int encfs_release(const char *path, struct fuse_file_info *finfo) {
EncFS_Context *ctx = context();
try {
ctx->eraseNode(path, reinterpret_cast<FileNode *>(finfo->fh));
auto fnode = ctx->lookupFuseFh(finfo->fh);
ctx->eraseNode(path, fnode);
return ESUCCESS;
} catch (encfs::Error &err) {
RLOG(ERROR) << "error caught in release: " << err.what();