void hf3fs_readdirplus()

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