in src/fuse/FuseOps.cc [2173:2374]
void hf3fs_readdirplus(fuse_req_t req, fuse_ino_t fino, size_t size, off_t off, struct fuse_file_info *fi) {
static constexpr off_t kOffsetBegin = 2;
if (off > kOffsetBegin) {
off -= kOffsetBegin;
}
auto ino = real_ino(fino);
auto did = ((DirHandle *)fi->fh)->dirId;
XLOGF(OP_LOG_LEVEL,
"hf3fs_readdirplus(ino={}, size={}, off={}, fh={}, pid={})",
ino,
size,
off,
did,
fuse_req_ctx(req)->pid);
record("readdirplus", fuse_req_ctx(req)->uid);
auto userInfo = UserInfo(flat::Uid(fuse_req_ctx(req)->uid), flat::Gid(fuse_req_ctx(req)->gid), d.fuseToken);
std::shared_ptr<std::vector<DirEntry>> pe;
std::shared_ptr<std::vector<std::optional<Inode>>> pi;
bool needsRpc = false;
const Inode *pino;
auto dname = checkVirtDir(ino, &pino);
if (dname) {
if (*dname == virtDir) {
static folly::atomic_shared_ptr<std::vector<DirEntry>> rootEntries;
static folly::atomic_shared_ptr<std::vector<std::optional<Inode>>> rootChildren;
if (rootChildren.load()) {
pe = rootEntries.load();
pi = rootChildren.load();
} else {
pe = std::make_shared<std::vector<DirEntry>>();
pi = std::make_shared<std::vector<std::optional<Inode>>>();
pe->reserve(topVirtDirs.size());
pi->reserve(topVirtDirs.size());
for (const auto &ni : topVirtDirs) {
pe->push_back(DirEntry(ino, ni.name, {ni.inode.id, ni.inode.data().getType(), ni.inode.data().acl}));
pi->push_back(ni.inode);
}
rootEntries.store(pe);
rootChildren.store(pi);
}
} else if (*dname == "iovs") {
std::tie(pe, pi) = d.iovs.listIovs(userInfo);
std::lock_guard lock{d.readdirplusResultsMutex};
d.readdirplusResults.insert({did, DirEntryInodeVector{pe, pi}});
} else if (*dname == "get-conf") {
std::tie(pe, pi) = d.userConfig.listConfig(userInfo);
std::lock_guard lock{d.readdirplusResultsMutex};
d.readdirplusResults.insert({did, DirEntryInodeVector{pe, pi}});
} else {
pe = std::make_shared<std::vector<DirEntry>>();
}
}
if (!pe) {
std::lock_guard lock{d.readdirplusResultsMutex};
auto it = d.readdirplusResults.find(did);
if (it == d.readdirplusResults.end()) {
needsRpc = true;
}
}
if (needsRpc) {
std::vector<DirEntry> entries;
std::vector<std::optional<Inode>> inodes;
bool hasNext = true;
std::string_view prev;
while (hasNext) {
auto ret = withRequestInfo(req, d.metaClient->list(userInfo, ino, std::nullopt, prev, 256, false));
if (ret.hasError()) {
handle_error(req, ret);
return;
} else {
for (auto &ent : ret->entries) {
entries.push_back(std::move(ent));
inodes.push_back(std::nullopt);
}
hasNext = ret->more;
if (hasNext) {
prev = entries.back().name;
}
}
}
if (ino == InodeId::root()) {
entries.push_back(
DirEntry(ino, virtDir, {virtDirInode.id, virtDirInode.data().getType(), virtDirInode.data().acl}));
inodes.push_back(virtDirInode);
}
std::lock_guard lock{d.readdirplusResultsMutex};
d.readdirplusResults.insert(
{did,
DirEntryInodeVector{std::make_shared<std::vector<DirEntry>>(std::move(entries)),
std::make_shared<std::vector<std::optional<Inode>>>(std::move(inodes))}});
}
auto rem = size;
auto buf = std::make_unique<char[]>(size);
auto p = buf.get();
if (!pe) {
std::lock_guard lock{d.readdirplusResultsMutex};
pe = d.readdirplusResults.find(did)->second.dirEntries;
pi = d.readdirplusResults.find(did)->second.inodes;
}
const auto &entries = *pe;
const auto &inodes = *pi;
off_t last = off;
std::vector<std::optional<Inode>> realInodes;
int64_t cursize = 0;
while ((size_t)last < entries.size()) {
#define FUSE_NAME_OFFSET_DIRENTPLUS 152
#define FUSE_REC_ALIGN(x) (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
auto entsize = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + entries[last].name.size());
if (cursize + entsize < size) {
cursize += entsize;
last++;
} else {
break;
}
}
std::vector<InodeId> queryIds;
for (auto i = off; i < last; i++) {
if (!inodes[i].has_value()) {
queryIds.push_back(entries[i].id);
}
}
auto queryRet = withRequestInfo(req, d.metaClient->batchStat(userInfo, queryIds));
if (queryRet.hasError()) {
handle_error(req, queryRet);
return;
}
for (auto i = off, cur = 0L; i < last; i++) {
if (!inodes[i].has_value()) {
realInodes.push_back(queryRet.value()[cur++]);
} else {
realInodes.push_back(inodes[i]);
}
}
for (auto i = off; i < last; i++) {
auto idx = i - off;
std::string name;
struct fuse_entry_param e;
const auto &ent = entries[i];
name = ent.name;
if (realInodes[idx].has_value()) {
if (realInodes[idx]->isSymlink()) {
init_entry(&e,
d.userConfig.getConfig(userInfo).symlink_timeout(),
d.userConfig.getConfig(userInfo).symlink_timeout());
} else {
init_entry(&e,
d.userConfig.getConfig(userInfo).attr_timeout(),
d.userConfig.getConfig(userInfo).entry_timeout());
}
XLOGF(DBG,
"inode id {} uid {} permission {}",
realInodes[idx]->id,
realInodes[idx]->data().acl.uid,
realInodes[idx]->data().acl.perm);
add_entry(realInodes[idx].value(), &e);
XLOGF(DBG, "entry id {} uid {} permission {}", e.attr.st_ino, e.attr.st_uid, e.attr.st_mode);
// std::cout << "adding dentry " << name << " i " << i << " size " << entries.size() << std::endl;
auto entsize = fuse_add_direntry_plus(req, p, rem, name.data(), &e, i + 1 + kOffsetBegin);
if (entsize > rem) {
remove_entry(realInodes[idx]->id, 1);
break;
}
p += entsize;
rem -= entsize;
} else {
add_empty_entry(entries[i].id);
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
auto entsize = fuse_add_direntry_plus(req, p, rem, name.data(), &e, i + 1 + kOffsetBegin);
if (entsize > rem) {
remove_entry(entries[i].id, 1);
break;
}
p += entsize;
rem -= entsize;
}
}
fuse_reply_buf(req, buf.get(), size - rem);
}