From e2f0f8e3c68604a46d8665c3b0125bb35a0d9181 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 22 Jul 2017 19:19:21 +0200 Subject: [PATCH] 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. --- encfs/Context.cpp | 32 +++++++++++++++++++++++++++----- encfs/Context.h | 11 +++++++++-- encfs/DirNode.cpp | 3 ++- encfs/FileNode.cpp | 5 ++++- encfs/FileNode.h | 5 ++++- encfs/encfs.cpp | 25 ++++++++++++++++--------- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/encfs/Context.cpp b/encfs/Context.cpp index b4153c2..5ba651a 100644 --- a/encfs/Context.cpp +++ b/encfs/Context.cpp @@ -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 &&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 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 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 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 diff --git a/encfs/Context.h b/encfs/Context.h index fade081..ff1cd0f 100644 --- a/encfs/Context.h +++ b/encfs/Context.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "encfs.h" @@ -46,9 +47,9 @@ class EncFS_Context { void getAndResetUsageCounter(int *usage, int *openCount); - FileNode *putNode(const char *path, std::shared_ptr &&node); + void putNode(const char *path, std::shared_ptr node); - void eraseNode(const char *path, FileNode *fnode); + void eraseNode(const char *path, std::shared_ptr 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 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 root; + + std::atomic currentFuseFh; + std::unordered_map> fuseFhMap; }; int remountFS(EncFS_Context *ctx); diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp index ad9a6e7..1692fd9 100644 --- a/encfs/DirNode.cpp +++ b/encfs/DirNode.cpp @@ -644,8 +644,9 @@ std::shared_ptr 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); diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp index 39277d7..e407d3b 100644 --- a/encfs/FileNode.cpp +++ b/encfs/FileNode.cpp @@ -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 rawIO(new RawFileIO(_cname)); io = std::shared_ptr(new CipherFileIO(rawIO, fsConfig)); diff --git a/encfs/FileNode.h b/encfs/FileNode.h index 0ff677b..b522200 100644 --- a/encfs/FileNode.h +++ b/encfs/FileNode.h @@ -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 canary; + // FUSE file handle that is passed to the kernel + uint64_t fuseFh; + const char *plaintextName() const; const char *cipherName() const; diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp index 6aee9dd..3b2e308 100644 --- a/encfs/encfs.cpp +++ b/encfs/encfs.cpp @@ -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 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(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(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(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();