void hf3fs_ioctl()

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