Skip to content

Commit f2f9405

Browse files
committed
Revise the logic of readdir to make sure each dirent has a unique cookie, and recycle expired readdir contexts
1 parent 678b915 commit f2f9405

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

src/directory.cpp

+26-13
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,16 @@ int Directory::ReadAndReply(fuse_req_t req, size_t size, off_t off) {
146146
return fuse_reply_err(req, EISDIR);
147147
}
148148

149+
/* The cookie uses 32 bits, with the higher 16 bit the key to the readdir
150+
* context, and the lower 16 bit representing the index of the directory entry.
151+
*/
149152
Directory::ReadDirCtx* Directory::PrepareReaddir(off_t cookie) {
153+
off_t key = 0;
150154
if (cookie != 0) {
151155
/* NOTE: Will throw std::out_of_range if no entry is found */
152-
Directory::ReadDirCtx* ctx = readdirStates.at(cookie);
153-
154-
/* If we have reached the end of the iterator, we should destroy this context
155-
* to release memory */
156-
if (ctx->it == ctx->children.end()) {
157-
Directory::readdirStates.erase(cookie);
158-
delete ctx;
159-
throw(std::out_of_range("Not found"));
160-
}
156+
key = (cookie & 0xffff0000) >> 16;
157+
Directory::ReadDirCtx* ctx = readdirStates.at(key);
158+
161159
return ctx;
162160
}
163161
/* Make a copy of children */
@@ -166,16 +164,31 @@ Directory::ReadDirCtx* Directory::PrepareReaddir(off_t cookie) {
166164
lk.unlock();
167165

168166
/* Add it to the table */
169-
cookie = rand();
167+
key = rand() & 0xffff;
170168
/* Make sure there is no duplicate */
171-
while (readdirStates.find(cookie) != readdirStates.end()) {
172-
cookie = rand();
169+
while (readdirStates.find(key) != readdirStates.end()) {
170+
key = rand() & 0xffff;
173171
}
172+
cookie = 0;
173+
cookie = key << 16;
174174
ReadDirCtx *newctx = new ReadDirCtx(cookie, copiedChildren);
175-
readdirStates.insert({cookie, newctx});
175+
readdirStates.insert({key, newctx});
176176
return newctx;
177177
}
178178

179+
void Directory::RecycleStates() {
180+
// find context objects whose iterators have reached the end, and free them
181+
for (auto ctxiter = readdirStates.begin(); ctxiter != readdirStates.end();) {
182+
Directory::ReadDirCtx *ctx = ctxiter->second;
183+
if (ctx->it == ctx->children.end()) {
184+
delete ctx;
185+
ctxiter = readdirStates.erase(ctxiter);
186+
} else {
187+
++ctxiter;
188+
}
189+
}
190+
}
191+
179192
bool Directory::IsEmpty() {
180193
std::shared_lock<std::shared_mutex> lk(childrenRwSem);
181194
for (auto it = m_children.begin(); it != m_children.end(); ++it) {

src/directory.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Directory : public Inode {
2828

2929
static std::unordered_map<off_t, Directory::ReadDirCtx *> readdirStates;
3030
ReadDirCtx* PrepareReaddir(off_t cookie);
31+
void RecycleStates();
3132
friend class FuseRamFs;
3233
public:
3334
~Directory() {}

src/fuse_cpp_ramfs.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ void FuseRamFs::FuseReadDir(fuse_req_t req, fuse_ino_t ino, size_t size,
682682
bufSize - bytesAdded,
683683
ctx->it->first.c_str(),
684684
&stbuf,
685-
ctx->cookie);
685+
++ctx->cookie);
686686
if (bytesAdded > bufSize) {
687687
// Oops. There wasn't enough space for that last item. Back up and exit.
688688
--(ctx->it);
@@ -694,6 +694,7 @@ void FuseRamFs::FuseReadDir(fuse_req_t req, fuse_ino_t ino, size_t size,
694694
}
695695
}
696696

697+
dir->RecycleStates();
697698
fuse_reply_buf(req, buf, bytesAdded);
698699
std::free(buf);
699700
}

0 commit comments

Comments
 (0)