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