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();