void hf3fs_symlink()

in src/fuse/FuseOps.cc [1160:1319]


void hf3fs_symlink(fuse_req_t req, const char *link, fuse_ino_t fparent, const char *name) {
  auto userInfo = UserInfo(flat::Uid(fuse_req_ctx(req)->uid), flat::Gid(fuse_req_ctx(req)->gid), d.fuseToken);

  auto parent = real_ino(fparent);
  auto pid = fuse_req_ctx(req)->pid;

  XLOGF(OP_LOG_LEVEL, "hf3fs_symlink(link={}, parent={}, name={}, pid={})", link, parent, name, pid);
  record("symlink", fuse_req_ctx(req)->uid);

  static const auto mountPoint = d.fuseMountpoint.lexically_normal();
  static const auto remountPref =
      d.fuseRemountPref ? std::make_optional(d.fuseRemountPref->lexically_normal()) : std::nullopt;

  struct fuse_entry_param e;
  init_entry(&e,
             d.userConfig.getConfig(userInfo).symlink_timeout(),
             d.userConfig.getConfig(userInfo).symlink_timeout());

  auto dname = checkVirtDir(parent);
  if (dname) {
    if (*dname == "rm-rf") {
      if (d.userConfig.getConfig(userInfo).readonly()) {
        fuse_reply_err(req, EROFS);
        return;
      }

      if (d.config->check_rmrf() && !check_rmrf(fuse_req_ctx(req)->pid)) {
        fuse_reply_err(req, EINVAL);
        return;
      }
      XLOGF(INFO,
            "Try to recursive remove {}, uid {}, cmdline {}",
            link,
            fuse_req_ctx(req)->uid,
            proc_cmdline(fuse_req_ctx(req)->pid));

      auto res = extractServerDirPath(req, link, userInfo);
      if (res.hasError()) {
        handle_error(req, res);
      } else {
        auto res2 = withRequestInfo(req, d.metaClient->remove(userInfo, res->inode.id, res->name, true));
        if (res2.hasError()) {
          handle_error(req, res2);
        } else {
          //          add_entry(Inode{InodeId{-(100ull << 30) - res->inode.id.u64()}, InodeData{meta::Symlink{link}}},
          //          &e);
          fillLinuxStat(e.attr, Inode{InodeId::virtTemporary(res->inode.id), InodeData{meta::Symlink{link}}});
          e.ino = e.attr.st_ino;

          // at most 1 thread may wait on lock
          notify_inval_entry(res->inode.id, res->name);
          fuse_reply_entry(req, &e);
          d.notifyInvalExec->add([parent, name = std::string(name)]() { notify_inval_entry(parent, name); });
        }
      }
    } else if (*dname == "iovs") {
      auto res = d.iovs.addIov(name,
                               Path(link),
                               pid,
                               userInfo,
                               &d.client->tpg().bgThreadPool().randomPick(),
                               *d.storageClient);
      if (res.hasError()) {
        handle_error(req, res);
      } else {
        auto &[inode, ior] = *res;
        if (ior) {
          auto res2 = d.iors.addIoRing(d.fuseRemountPref.value_or(d.fuseMountpoint),
                                       res->second,
                                       name,
                                       userInfo,
                                       ior->forRead,
                                       ior->bufStart,
                                       ior->size,
                                       ior->ioDepth,
                                       *ior->iora);
          if (!res2) {
            handle_error(req, res);
          }
          // record the ior index for later removal
          res->second->iorIndex = *res2;
        }
        //        add_entry(inode, &e);
        fillLinuxStat(e.attr, inode);
        e.ino = e.attr.st_ino;
        fuse_reply_entry(req, &e);
      }
    } else if (*dname == "set-conf") {
      auto res = d.userConfig.setConfig(name, link, userInfo);
      if (res.hasError()) {
        handle_error(req, res);
      } else {
        XLOGF(DBG, "done set config");
        fillLinuxStat(e.attr, *res);
        XLOGF(DBG, "filled linux stat");
        e.ino = e.attr.st_ino;
        fuse_reply_entry(req, &e);
        XLOGF(DBG, "replied entry");

        // // invalidate the link after replying, we don't actually want a link under the set conf dir
        // fuse_lowlevel_notify_inval_entry(d.se, e.ino, name, strlen(name));
        // fuse_lowlevel_notify_inval_inode(d.se, e.ino, -1, 0);
      }
    } else {
      fuse_reply_err(req, EPERM);
    }
    return;
  }

  if (d.userConfig.getConfig(userInfo).readonly()) {
    fuse_reply_err(req, EROFS);
    return;
  }

  static const std::string mvPref = "mv:";
  if (!strncmp(link, mvPref.c_str(), mvPref.size())) {
    auto rlink = link + mvPref.size();
    auto res = extractServerDirPath(req, rlink, userInfo);
    if (res.hasError()) {
      handle_error(req, res);
    } else {
      auto res2 = withRequestInfo(req, [&userInfo, &res, parent, name]() -> CoTryTask<Inode> {
        co_return co_await d.metaClient->rename(userInfo, res->inode.id, res->name, parent, name);
        //        co_return co_await (d.metaClient->stat(userInfo, parent, name, false));
      }());

      if (res2.hasError()) {
        handle_error(req, res2);
      } else {
        auto viid = InodeId::virtTemporary(res->inode.id);
        //        add_entry(Inode{viid, InodeData{meta::Symlink{rlink}}}, &e);
        // add_entry(*res2, &e);
        fillLinuxStat(e.attr, Inode{viid, InodeData{meta::Symlink{rlink}}});
        e.ino = e.attr.st_ino;

        d.notifyInvalExec->add([res, viid, parent, name = std::string(name), req, e]() {
          if (res->inode.id != parent) {
            notify_inval_entry(res->inode.id, res->name);
            fuse_reply_entry(req, &e);
          } else {
            fuse_reply_entry(req, &e);
            notify_inval_entry(res->inode.id, res->name);
          }
          notify_inval_entry(parent, name);
          notify_inval_inode(viid);
        });
      }
    }

    return;
  }

  auto res = withRequestInfo(req, d.metaClient->symlink(userInfo, parent, name, link));
  if (res.hasError()) {
    handle_error(req, res);
  } else {
    add_entry(res.value(), &e);
    fuse_reply_entry(req, &e);
  }
}