in src/fuse/FuseOps.cc [1908:2171]
void hf3fs_ioctl(fuse_req_t req,
fuse_ino_t fino,
unsigned int cmd,
void *arg,
struct fuse_file_info *fi,
unsigned flags,
const void *in_buf,
size_t in_bufsz,
size_t out_bufsz) {
(void)fi;
(void)in_bufsz;
auto ino = real_ino(fino);
XLOGF(OP_LOG_LEVEL, "hf3fs_ioctl(ino={}, pid={})", ino, fuse_req_ctx(req)->pid);
record("ioctl", fuse_req_ctx(req)->uid);
if (flags & FUSE_IOCTL_COMPAT) {
fuse_reply_err(req, ENOSYS);
return;
}
auto userInfo = UserInfo(flat::Uid(fuse_req_ctx(req)->uid), flat::Gid(fuse_req_ctx(req)->gid), d.fuseToken);
switch (cmd) {
case FS_IOC_GETFLAGS: {
if (out_bufsz < sizeof(int)) {
struct iovec iov = {arg, sizeof(int)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
auto res = withRequestInfo(req, d.metaClient->stat(userInfo, ino, {}, false));
if (res.hasError()) {
handle_error(req, res);
} else {
int iflags = res->acl.iflags;
fuse_reply_ioctl(req, 0, &iflags, sizeof(iflags));
}
}
break;
}
case FS_IOC_SETFLAGS: {
if (in_bufsz < sizeof(int)) {
fuse_reply_err(req, EINVAL);
} else {
int iflags = *(int *)in_buf;
auto res = withRequestInfo(req, d.metaClient->setIFlags(userInfo, ino, meta::IFlags(iflags)));
if (res.hasError()) {
handle_error(req, res);
} else {
fuse_reply_ioctl(req, 0, nullptr, 0);
}
}
break;
}
case FS_IOC_FSGETXATTR: {
if (out_bufsz < sizeof(fsxattr)) {
struct iovec iov = {arg, sizeof(fsxattr)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
auto res = withRequestInfo(req, d.metaClient->stat(userInfo, ino, {}, false));
if (res.hasError()) {
handle_error(req, res);
} else {
fsxattr xattr{};
if (res->acl.iflags & FS_IMMUTABLE_FL) {
xattr.fsx_xflags |= FS_XFLAG_IMMUTABLE;
}
fuse_reply_ioctl(req, 0, &xattr, sizeof(xattr));
}
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_GET_MOUNT_NAME: {
if (!out_bufsz) {
struct iovec iov = {arg, sizeof(hf3fs::lib::fuse::Hf3fsIoctlGetMountNameArg)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
hf3fs::lib::fuse::Hf3fsIoctlGetMountNameArg ret;
strcpy(ret.str, d.fuseMount.c_str());
fuse_reply_ioctl(req, 0, &ret, sizeof(ret));
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_GET_PATH_OFFSET: {
if (!out_bufsz) {
struct iovec iov = {arg, sizeof(uint32_t)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
uint32_t offset = d.fuseMountpoint.size();
fuse_reply_ioctl(req, 0, &offset, sizeof(offset));
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_GET_MAGIC_NUM: {
if (!out_bufsz) {
struct iovec iov = {arg, sizeof(uint32_t)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
uint32_t magic = HF3FS_SUPER_MAGIC;
fuse_reply_ioctl(req, 0, &magic, sizeof(magic));
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_GET_IOCTL_VERSION: {
// add a get version ioctl, application can check ioctl cmd is supported or not
if (!out_bufsz) {
struct iovec iov = {arg, sizeof(uint32_t)};
fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
} else {
uint32_t version = 1;
fuse_reply_ioctl(req, 0, &version, sizeof(version));
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_RECURSIVE_RM: {
// if (!out_bufsz) {
// struct iovec iov = {arg, sizeof(uint32_t)};
// fuse_reply_ioctl_retry(req, nullptr, 0, &iov, 1);
// } else {
auto res = withRequestInfo(req, [&userInfo, ino]() -> CoTryTask<void> {
auto res = co_await d.metaClient->getRealPath(userInfo, ino, std::nullopt, true);
if (res.hasError()) {
co_return makeError(res.error());
}
co_return co_await d.metaClient->remove(userInfo, InodeId::root(), *res, true);
}());
if (res.hasError()) {
handle_error(req, res);
} else {
fuse_reply_ioctl(req, 0, nullptr, 0);
}
// }
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_FSYNC: {
auto userInfo = UserInfo(flat::Uid(fuse_req_ctx(req)->uid), flat::Gid(fuse_req_ctx(req)->gid), d.fuseToken);
auto pi = inodeOf(ino);
auto res = flushAndSync(req, linux_ino(ino), false, SyncType::ForceFsync, nullptr);
notify_inval_inode(ino);
if (res) {
fuse_reply_ioctl(req, 0, nullptr, 0);
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_HARDLINK: {
auto newParent = InodeId(((hf3fs::lib::fuse::Hf3fsIoctlHardlinkArg *)in_buf)->ino);
const char *name = ((hf3fs::lib::fuse::Hf3fsIoctlHardlinkArg *)in_buf)->str;
auto res =
withRequestInfo(req, d.metaClient->hardLink(userInfo, ino, std::nullopt, newParent, Path(name), false));
if (res.hasError()) {
handle_error(req, res);
} else {
fuse_reply_ioctl(req, 0, nullptr, 0);
}
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_PUNCH_HOLE: {
auto userInfo = UserInfo(flat::Uid(fuse_req_ctx(req)->uid), flat::Gid(fuse_req_ctx(req)->gid), d.fuseToken);
auto arg = (hf3fs::lib::fuse::Hf3fsIoctlPunchHoleArg *)in_buf;
std::vector<storage::client::RemoveChunksOp> removeOps;
auto pi = inodeOf(ino);
auto file = pi->inode.asFile();
auto chunkSize = file.layout.chunkSize;
auto routingInfo = d.mgmtdClient->getRoutingInfo();
for (int i = 0; i < arg->n; i++) {
if (arg->start[i] % chunkSize != 0 || arg->end[i] % chunkSize != 0) {
fuse_reply_err(req, EINVAL);
return;
}
for (size_t off = arg->start[i]; off < arg->end[i]; off += chunkSize) {
auto chainIdRes = file.getChainId(pi->inode, off, *routingInfo->raw());
if (chainIdRes.hasError()) {
handle_error(req, chainIdRes);
return;
}
auto chainId = *chainIdRes;
auto chunkId = file.getChunkId(ino, off);
if (chunkId.hasError()) {
handle_error(req, chunkId);
return;
}
auto removeOp = d.storageClient->createRemoveOp(chainId,
storage::ChunkId(*chunkId),
storage::ChunkId(storage::ChunkId(*chunkId), 1));
removeOps.push_back(std::move(removeOp));
}
}
auto res = withRequestInfo(req, d.storageClient->removeChunks(removeOps, userInfo));
if (res.hasError()) {
handle_error(req, res);
return;
}
for (auto &op : removeOps) {
if (!op.status().isOK()) {
fuse_reply_err(req, EIO);
return;
}
}
fuse_reply_ioctl(req, 0, nullptr, 0);
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_MOVE: {
if (in_bufsz != sizeof(hf3fs::lib::fuse::Hf3fsIoctlMove)) {
fuse_reply_err(req, EINVAL);
return;
}
auto move = (const hf3fs::lib::fuse::Hf3fsIoctlMove *)(in_buf);
auto srcParent = real_ino(move->srcParent);
auto srcName = Path(getCString(move->srcName, NAME_MAX));
auto dstParent = real_ino(move->dstParent);
auto dstName = Path(getCString(move->dstName, NAME_MAX));
auto moveToTrash = move->moveToTrash;
if (srcParent != ino) {
fuse_reply_err(req, EINVAL);
return;
}
if (srcName.has_parent_path() || dstName.has_parent_path()) {
fuse_reply_err(req, EINVAL);
return;
}
auto res =
withRequestInfo(req, d.metaClient->rename(userInfo, srcParent, srcName, dstParent, dstName, moveToTrash));
if (res.hasError()) {
handle_error(req, res);
return;
}
d.notifyInvalExec->add([srcParent, dstParent, srcName, dstName, req]() {
notify_inval_entry(srcParent, srcName.string());
notify_inval_entry(dstParent, dstName.string());
fuse_reply_ioctl(req, 0, nullptr, 0);
});
break;
}
case hf3fs::lib::fuse::HF3FS_IOC_REMOVE: {
if (in_bufsz != sizeof(hf3fs::lib::fuse::Hf3fsIoctlRemove)) {
fuse_reply_err(req, EINVAL);
return;
}
auto remove = (const hf3fs::lib::fuse::Hf3fsIoctlRemove *)(in_buf);
auto parent = real_ino(remove->parent);
auto name = Path(getCString(remove->name, NAME_MAX));
auto recursive = remove->recursive;
if (parent != ino) {
fuse_reply_err(req, EINVAL);
return;
}
if (name.has_parent_path()) {
fuse_reply_err(req, EINVAL);
return;
}
auto res = withRequestInfo(req, d.metaClient->remove(userInfo, parent, name, recursive));
if (res.hasError()) {
handle_error(req, res);
return;
}
d.notifyInvalExec->add([parent, name, req]() {
notify_inval_entry(parent, name.string());
fuse_reply_ioctl(req, 0, nullptr, 0);
});
break;
}
default:
fuse_reply_err(req, EINVAL);
}
}