Yikun Jiang a68570b5d9 Add computing offloading code
1. Add computing offloading code
2. Add script.md
3. Add virsh_demo.xml

Change-Id: Id9ef883e2f0eb727eb5448b9d1c47767f46b1021
Signed-off-by: Yikun Jiang <yikunkero@gmail.com>
2023-10-23 19:29:57 +08:00

1669 lines
46 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/time.h>
#include <linux/fs_struct.h>
#include <linux/statfs.h>
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include <linux/wait.h>
#include <linux/version.h>
#include <asm-generic/ioctls.h>
#include <asm-generic/termbits.h>
#include <linux/if_tun.h>
#include "conn.h"
#include "qtfs-mod.h"
#include "req.h"
#include "log.h"
#include "ops.h"
#include "symbol_wrapper.h"
#define CURRENT_TIME(inode) (current_time(inode))
static struct inode_operations qtfs_inode_ops;
static struct inode_operations qtfs_symlink_inode_ops;
struct inode *qtfs_iget(struct super_block *sb, struct inode_info *ii);
extern ssize_t qtfs_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size);
int qtfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_statfs *req;
struct qtrsp_statfs *rsp;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
rsp = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_RECV);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
rsp = qtfs_remote_run(pvar, QTFS_REQ_STATFS, QTFS_SEND_SIZE(struct qtreq_statfs, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return -EINVAL;
}
if (rsp->ret == QTFS_ERR) {
int ret = rsp->errno;
qtfs_err("qtfs statfs failed. %d", rsp->errno);
qtfs_conn_put_param(pvar);
return ret;
}
qtfs_info("%s: get path %s\n", __func__, req->path);
memcpy(buf, &(rsp->kstat), sizeof(struct kstatfs));
qtfs_conn_put_param(pvar);
return 0;
}
static void qtfs_free_inode(struct inode *inode)
{
if (inode->i_private) {
kmem_cache_free(qtfs_inode_priv_cache, inode->i_private);
inode->i_private = NULL;
}
free_inode_nonrcu(inode);
return;
}
static const struct super_operations qtfs_ops = {
.statfs = qtfs_statfs,
.free_inode = qtfs_free_inode,
};
static inline struct qtfs_fs_info *qtfs_priv_byinode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
return sb->s_fs_info;
}
static inline char *qtfs_mountpoint_path_init(struct dentry *dentry, struct path *path, char *mnt_file)
{
char *name = NULL;
char *ret;
char *mnt_point;
size_t len;
struct qtfs_fs_info *fsinfo = qtfs_priv_byinode(d_inode(dentry));
if (fsinfo && fsinfo->mnt_path) {
return fsinfo->mnt_path;
}
name = __getname();
if (!name) {
return ERR_PTR(-ENOMEM);
}
path_get(path);
ret = qtfs_kern_syms.d_absolute_path(path, name, MAX_PATH_LEN);
qtfs_debug("mntfile:%s absolute:%s", mnt_file, ret);
if (IS_ERR_OR_NULL(ret)) {
qtfs_err("d_absolute_path failed:%ld", QTFS_PTR_ERR(ret));
} else {
if (strcmp(mnt_file, "/")) {
mnt_point = strstr(ret, mnt_file);
qtfs_info("mnt point:%s", mnt_point);
if (mnt_point) {
*mnt_point = '\0';
} else {
qtfs_err("Failed to get mount root path");
}
}
len = strlen(ret);
if (len == 0) {
qtfs_err("mount path len invalid.");
goto end;
}
fsinfo->mnt_path = (char *)kmalloc(len + 1, GFP_KERNEL);
if (fsinfo->mnt_path) {
strlcpy(fsinfo->mnt_path, ret, len + 1);
}
qtfs_debug("d_absolute_path get mnt path:%s", fsinfo->mnt_path);
}
end:
path_put(path);
__putname(name);
return fsinfo->mnt_path;
}
int qtfs_readdir(struct file *filp, struct dir_context *ctx)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_readdir *req;
struct qtrsp_readdir *rsp;
struct qtfs_dirent64 *dirent = NULL;
int idx;
int ret;
int namelen;
int dircnt;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return -EINVAL;
}
if (ctx->pos == -1) {
qtfs_conn_put_param(pvar);
return -ENOENT;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
rsp = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_RECV);
QTFS_FULLNAME(req->path, filp->f_path.dentry, sizeof(req->path));
req->count = sizeof(rsp->dirent);
req->pos = ctx->pos;
rsp = qtfs_remote_run(pvar, QTFS_REQ_READDIR, QTFS_SEND_SIZE(struct qtreq_readdir, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->d.ret == QTFS_ERR || rsp->d.vldcnt < 0 || rsp->d.pos < 0) {
qtfs_err("qtfs readdir failed.");
qtfs_conn_put_param(pvar);
return -EFAULT;
}
idx = 0;
dircnt = rsp->d.vldcnt;
while (dircnt-- > 0) {
if (idx >= sizeof(rsp->dirent)) {
qtfs_err("invalid idx:%d", idx);
break;
}
dirent = (struct qtfs_dirent64 *)&rsp->dirent[idx];
namelen = strlen(dirent->d_name);
ret = dir_emit(ctx, dirent->d_name, namelen,
dirent->d_ino, dirent->d_type);
idx += dirent->d_reclen;
qtfs_debug("qtfs readdir direntoff:0x%lx name:<%s>, ret:%d, reclen:%u namelen:%d, ino:%llu type:%d",
(void *)dirent - (void *)rsp->dirent, dirent->d_name, ret, dirent->d_reclen, namelen, dirent->d_ino, dirent->d_type);
}
ctx->pos = (rsp->d.over) ? -1 : rsp->d.pos;
qtfs_info("qtfs readdir<%s> success ret:%d vldcnt:%d over:%d pos:%lld.",
req->path, rsp->d.ret, rsp->d.vldcnt, rsp->d.over, ctx->pos);
qtfs_conn_put_param(pvar);
return 0;
}
int qtfs_open(struct inode *inode, struct file *file)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_open *req;
struct qtrsp_open *rsp;
struct private_data *data = NULL;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, file->f_path.dentry, sizeof(req->path));
req->flags = file->f_flags;
req->mode = file->f_mode;
rsp = qtfs_remote_run(pvar, QTFS_REQ_OPEN, QTFS_SEND_SIZE(struct qtreq_open, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
qtfs_err("qtfs open:%s failed, f_mode:%o flag:%x", req->path, file->f_mode, file->f_flags);
return -EINVAL;
}
if (rsp->ret == QTFS_ERR) {
int err = rsp->fd;
if (rsp->fd != -ENOENT) {
qtfs_err("qtfs_open failed with %d ret:%d", rsp->fd, rsp->ret);
} else {
qtfs_info("qtfs_open file %s failed, not exist.", req->path);
}
qtfs_conn_put_param(pvar);
return err;
}
qtfs_info("qtfs open:%s success, f_mode:%o flag:%x, fd:%d", req->path, file->f_mode, file->f_flags, rsp->fd);
data = (struct private_data *)kmalloc(sizeof(struct private_data), GFP_KERNEL);
if (IS_ERR_OR_NULL(data)) {
qtfs_err("qtfs_open alloc private_data failed: %ld", QTFS_PTR_ERR(data));
qtfs_conn_put_param(pvar);
return -ENOMEM;
}
data->fd = rsp->fd;
WARN_ON(file->private_data);
file->private_data = data;
qtfs_conn_put_param(pvar);
return 0;
}
int qtfs_dir_open(struct inode *inode, struct file *file)
{
qtfs_info("qtfs dir open enter: %s.", file->f_path.dentry->d_iname);
return 0;
}
int qtfs_dir_release(struct inode *inode, struct file *file)
{
qtfs_info("qtfs dir release enter: %s.", file->f_path.dentry->d_iname);
return 0;
}
int qtfs_release(struct inode *inode, struct file *file)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_close *req;
struct qtrsp_close *rsp;
struct private_data *private = NULL;
int ret;
if (pvar == NULL) {
qtfs_err("qtfs release pvar invalid.");
return -EFAULT;
}
if (IS_ERR_OR_NULL(file)) {
qtfs_err("qtfs release: invalid file: 0x%llx", (__u64)file);
qtfs_conn_put_param(pvar);
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
private = (struct private_data *)file->private_data;
if (IS_ERR_OR_NULL(private)) {
qtfs_err("qtfs_close(%s): invalid private_data pointer:%ld", file->f_path.dentry->d_iname, QTFS_PTR_ERR(private));
WARN_ON(1);
qtfs_conn_put_param(pvar);
return -EFAULT;
}
req->fd = private->fd;
rsp = qtfs_remote_run(pvar, QTFS_REQ_CLOSE, sizeof(struct qtreq_close));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_err("qtfs release fd:%d failed, rsp is invalid.", req->fd);
ret = QTFS_PTR_ERR(rsp);
goto end;
}
qtfs_info("qtfs release success fd:%d ret:%d %s", req->fd, rsp->ret, (rsp->ret == QTFS_ERR) ? "failed" : "success");
ret = rsp->ret;
end:
qtfs_conn_put_param(pvar);
kfree(file->private_data);
file->private_data = NULL;
return ret;
}
ssize_t qtfs_readiter(struct kiocb *kio, struct iov_iter *iov)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_readiter *req;
struct qtrsp_readiter *rsp;
int reqlen;
size_t leftlen = iov_iter_count(iov);
size_t allcnt = leftlen;
size_t tocnt = 0;
ssize_t ret;
struct private_data *private = NULL;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
private = (struct private_data *)kio->ki_filp->private_data;
if (IS_ERR_OR_NULL(private)) {
qtfs_err("qtfs_readiter(%s): invalid private_data pointer:%ld", kio->ki_filp->f_path.dentry->d_iname, QTFS_PTR_ERR(private));
qtfs_conn_put_param(pvar);
return -ENOMEM;
}
req->fd = private->fd;
if (req->fd <= 0) {
qtfs_err("qtfs_readiter: invalid file(%d)", req->fd);
qtfs_conn_put_param(pvar);
return -EINVAL;
}
reqlen = sizeof(struct qtreq_readiter);
do {
req->len = leftlen;
req->pos = kio->ki_pos;
rsp = qtfs_remote_run(pvar, QTFS_REQ_READITER, reqlen);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->d.ret == QTFS_ERR || rsp->d.len <= 0 || rsp->d.len > leftlen) {
if (rsp->d.len != 0)
qtfs_info("qtfs readiter error: %ld.", rsp->d.len);
ret = (rsp->d.len > leftlen) ? leftlen : (ssize_t)rsp->d.len;
qtfs_conn_put_param(pvar);
return (ret > 0) ? allcnt - leftlen + ret : allcnt - leftlen;
}
tocnt = copy_to_iter(rsp->readbuf, rsp->d.len, iov);
if (rsp->d.len != tocnt) {
qtfs_err("copy to iter failed, errno:%ld", tocnt);
qtfs_conn_put_param(pvar);
return allcnt - leftlen + tocnt;
}
leftlen -= rsp->d.len;
kio->ki_pos += rsp->d.len;
} while (leftlen > 0 && rsp->d.end == 0);
qtfs_info("qtfs readiter over, leftlen:%lu, reqlen:%lu, fullname:<%s>, ino:%lu, pos:%lld, iovcnt:%lu\n", leftlen,
req->len, kio->ki_filp->f_path.dentry->d_iname, kio->ki_filp->f_inode->i_ino, kio->ki_pos, iov_iter_count(iov));
qtfs_conn_put_param(pvar);
return allcnt - leftlen;
}
ssize_t qtfs_writeiter(struct kiocb *kio, struct iov_iter *iov)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_write *req;
struct qtrsp_write *rsp;
char *wrbuf = NULL;
int wrbuflen;
int maxbuflen;
size_t len = iov_iter_count(iov);
size_t leftlen = len;
struct private_data *private = NULL;
ssize_t ret;
struct file *filp;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var.");
return -EINVAL;
}
if (len <= 0) {
qtfs_conn_put_param(pvar);
return len;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
filp = kio->ki_filp;
private = (struct private_data *)filp->private_data;
if (IS_ERR_OR_NULL(private)) {
qtfs_err("qtfs_write(%s): invalid private_data pointer:%ld", filp->f_path.dentry->d_iname, QTFS_PTR_ERR(private));
qtfs_conn_put_param(pvar);
return -ENOMEM;
}
req->d.fd = private->fd;
if (req->d.fd < 0) {
qtfs_err("qtfs_write: invalid file(%d)", req->d.fd);
qtfs_conn_put_param(pvar);
return -EINVAL;
}
req->d.mode = filp->f_mode;
req->d.flags = filp->f_flags;
wrbuf = req->path_buf;
maxbuflen = sizeof(req->path_buf);
do {
req->d.total_len = len;
wrbuflen = (leftlen >= maxbuflen) ? (maxbuflen - 1) : leftlen;
req->d.buflen = wrbuflen;
req->d.pos = kio->ki_pos;
if (copy_from_iter(wrbuf, wrbuflen, iov) == 0) {
qtfs_err("qtfs write copy from iter failed, len:%d.", wrbuflen);
break;
}
rsp = qtfs_remote_run(pvar, QTFS_REQ_WRITE, sizeof(struct qtreq_write) - sizeof(req->path_buf) + wrbuflen);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->len > wrbuflen) {
qtfs_err("qtfs write recv error packet, len:%ld writelen:%d", rsp->len, wrbuflen);
break;
}
if (rsp->ret == QTFS_ERR || rsp->len <= 0) {
qtfs_err("qtfs write remote error, errno:%ld, leftlen:%lu.", rsp->len, leftlen);
if (rsp->len > 0) {
kio->ki_pos += rsp->len;
leftlen -= rsp->len;
break;
}
ret = rsp->len;
qtfs_conn_put_param(pvar);
return (ret > 0) ? len - leftlen + ret : len - leftlen;
}
if (rsp->len != wrbuflen) {
iov->count -= (rsp->len - wrbuflen);
iov->iov_offset += (rsp->len - wrbuflen);
}
kio->ki_pos += rsp->len;
leftlen -= rsp->len;
} while (leftlen > 0);
do {
struct inode *inode = kio->ki_filp->f_inode;
struct qtfs_inode_priv *priv = inode->i_private;
if (S_ISFIFO(inode->i_mode))
wake_up_interruptible_sync_poll(&priv->readq, EPOLLIN | EPOLLRDNORM);
if (S_ISCHR(inode->i_mode)) {
wake_up_interruptible_poll(&priv->readq, EPOLLIN);
qtfs_err("writeiter file:%s char:<%s> wakup poll.", filp->f_path.dentry->d_iname, req->path_buf);
}
} while (0);
qtfs_info("qtfs write %s over, leftlen:%lu.", filp->f_path.dentry->d_iname, leftlen);
qtfs_conn_put_param(pvar);
return len - leftlen;
}
loff_t qtfs_llseek(struct file *file, loff_t off, int whence)
{
struct qtfs_conn_var_s *pvar = NULL;
struct qtreq_llseek *req;
struct qtrsp_llseek *rsp;
off_t ret;
struct private_data *priv = NULL;
qtfs_info("qtfs llseek off:%lld, whence:%d cur pos:%lld.", off, whence, file->f_pos);
if (off == 0 && whence == SEEK_CUR) {
return file->f_pos;
}
pvar = qtfs_conn_get_param();
if (!pvar) {
qtfs_err("Failed to get qtfs sock var.");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
priv = (struct private_data *)file->private_data;
req->off = off;
req->whence = whence;
req->fd = priv->fd;
rsp = qtfs_remote_run(pvar, QTFS_REQ_LLSEEK, sizeof(struct qtreq_llseek));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
qtfs_err("Failed to remote run llseek.");
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret != QTFS_OK) {
ret = rsp->off;
qtfs_conn_put_param(pvar);
return ret;
}
file->f_pos = rsp->off;
ret = rsp->off;
qtfs_conn_put_param(pvar);
qtfs_info("qtfs llseek successed, cur seek pos:%ld.", ret);
return ret;
}
static void qtfs_vma_close(struct vm_area_struct *vma)
{
qtfs_info("qtfs vma close enter.");
filemap_write_and_wait(vma->vm_file->f_mapping);
}
static vm_fault_t qtfs_vm_fault(struct vm_fault *vmf)
{
vm_fault_t ret = filemap_fault(vmf);
qtfs_info("qtfs vm ops fault enter, filemap fault:0x%x, pgoff:%lu.", ret, vmf->pgoff);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
static vm_fault_t qtfs_map_pages(struct vm_fault *vmf,
pgoff_t start_pgoff, pgoff_t end_pgoff)
{
qtfs_info("qtfs map pages enter, pgoff:%lu start:%lu end:%lu.", vmf->pgoff, start_pgoff, end_pgoff);
return filemap_map_pages(vmf, start_pgoff, end_pgoff);
}
#else
static void qtfs_map_pages(struct vm_fault *vmf,
pgoff_t start_pgoff, pgoff_t end_pgoff)
{
qtfs_info("qtfs map pages enter, pgoff:%lu start:%lu end:%lu.", vmf->pgoff, start_pgoff, end_pgoff);
filemap_map_pages(vmf, start_pgoff, end_pgoff);
return;
}
#endif
static vm_fault_t qtfs_page_mkwrite(struct vm_fault *vmf)
{
qtfs_info("qtfs page mkwrite enter.");
return filemap_page_mkwrite(vmf);
}
static const struct vm_operations_struct qtfs_file_vm_ops = {
.fault = qtfs_vm_fault,
.map_pages = qtfs_map_pages,
.close = qtfs_vma_close,
.page_mkwrite = qtfs_page_mkwrite,
};
int qtfs_mmap(struct file *file, struct vm_area_struct *vma)
{
qtfs_info("qtfs mmap enter.");
if (IS_DAX(file_inode(file))) {
qtfs_info("qtfs mmap is dax mmap.");
}
file_accessed(file);
vma->vm_ops = &qtfs_file_vm_ops;
return 0;
}
int qtfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
qtfs_info("qtfs fsync enter.");
return 0;
}
long qtfs_do_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, unsigned int size, int argtype)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_ioctl *req;
struct qtrsp_ioctl *rsp;
unsigned int len = 0;
int ret = -EINVAL;
struct private_data *priv = NULL;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
rsp = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_RECV);
if (size >= sizeof(req->path)) {
qtfs_err("do ioctl failed, size:%u too big:%lu", size, sizeof(req->path));
qtfs_conn_put_param(pvar);
return -EINVAL;
}
priv = (struct private_data *)filp->private_data;
req->d.fd = priv->fd;
req->d.argtype = argtype;
req->d.cmd = cmd;
if (argtype) {
req->d.arg = arg;
len = sizeof(struct qtreq_ioctl) - sizeof(req->path);
} else if (size > 0) {
ret = copy_from_user(req->path, (char __user *)arg, size);
if (ret) {
qtfs_err("%s: copy_from_user, size %u failed.", __func__, size);
ret = -EFAULT;
goto out;
}
len = sizeof(struct qtreq_ioctl) - sizeof(req->path) + size;
req->d.size = size;
} else {
len = sizeof(struct qtreq_ioctl) - sizeof(req->path);
}
rsp = qtfs_remote_run(pvar, QTFS_REQ_IOCTL, len);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs ioctl cmd:0x%x failed. %d", cmd, rsp->errno);
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
qtfs_info("qtfs do ioctl cmd:0x%x success, path: %s size:%u, rsp size:%u", cmd, req->path, size, rsp->size);
ret = rsp->errno;
if (rsp->size > sizeof(rsp->buf) ||
(rsp->size > 0 && copy_to_user((char __user *)arg, rsp->buf, size))) {
qtfs_err("copy to user failed");
ret = -EFAULT;
}
out:
qtfs_conn_put_param(pvar);
return (long)ret;
}
#define QTFS_IOCTL_CASE_WITH_BREAK(size, argtype)\
{\
ret = qtfs_do_ioctl(filp, cmd, arg, size, argtype);\
break;\
}
long qtfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
long ret;
switch(cmd) {
// all case of size 0 type 0 enter here
case FS_IOC_FSGETXATTR:
case TCGETS:
QTFS_IOCTL_CASE_WITH_BREAK(0, 0);
// all case of size 0 type 1 enter here
case TUNSETPERSIST:
QTFS_IOCTL_CASE_WITH_BREAK(0, 1);
case FS_IOC_FSSETXATTR:
QTFS_IOCTL_CASE_WITH_BREAK(sizeof(struct fsxattr), 0);
case TCSETS:
QTFS_IOCTL_CASE_WITH_BREAK(sizeof(struct ktermios), 0);
case TUNSETIFF:
case SIOCGIFHWADDR:
case SIOCADDMULTI:
case SIOCBRADDIF:
case SIOCBRDELIF:
case TUNGETIFF:
case SIOCDELMULTI:
case SIOCDEVPRIVATE:
case SIOCETHTOOL:
case SIOCGIFADDR:
case SIOCGIFFLAGS:
case SIOCGIFINDEX:
case SIOCGIFMTU:
case SIOCSIFFLAGS:
case SIOCSIFHWADDR:
case SIOCSIFMTU:
case SIOCSIFNAME:
QTFS_IOCTL_CASE_WITH_BREAK(sizeof(struct ifreq), 0);
case SIOCBRADDBR:
case SIOCBRDELBR:
QTFS_IOCTL_CASE_WITH_BREAK(IFNAMSIZ, 0);
case SIOCGIFVLAN:
QTFS_IOCTL_CASE_WITH_BREAK(sizeof(struct vlan_ioctl_args), 0);
default: {
char *fullname = kmalloc(MAX_PATH_LEN, GFP_KERNEL);
if (!fullname)
return -ENOMEM;
memset(fullname, 0, MAX_PATH_LEN);
qtfs_fullname(fullname, filp->f_path.dentry, MAX_PATH_LEN);
qtfs_err("qtfs ioctl get not support cmd:%d file:%s", cmd, fullname);
kfree(fullname);
return -EOPNOTSUPP;
}
}
return ret;
}
loff_t qtfs_dir_file_llseek(struct file *file, loff_t offset, int whence)
{
qtfs_info("qtfs generic file llseek: %s.", file->f_path.dentry->d_iname);
return generic_file_llseek(file, offset, whence);
}
ssize_t qtfs_dir_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t *ppos)
{
qtfs_err("qtfs generic read dir: %s.", filp->f_path.dentry->d_iname);
return generic_read_dir(filp, buf, siz, ppos);
}
static struct file_operations qtfs_dir_ops = {
.owner = THIS_MODULE,
.iterate_shared = qtfs_readdir,
.unlocked_ioctl = qtfs_ioctl,
.open = qtfs_dir_open,
.release = qtfs_dir_release,
.llseek = qtfs_dir_file_llseek,
.read = qtfs_dir_read_dir,
};
static struct file_operations qtfs_file_ops = {
.read_iter = qtfs_readiter,
.write_iter = qtfs_writeiter,
.open = qtfs_open,
.release = qtfs_release,
.mmap = qtfs_mmap,
.llseek = qtfs_llseek,
.fsync = qtfs_fsync,
.unlocked_ioctl = qtfs_ioctl,
};
static int qtfs_readpage(struct file *file, struct page *page)
{
void *kaddr = NULL;
loff_t offset = page->index << PAGE_SHIFT;
qtfs_info("qtfs readpage enter, page pos:%lld.", offset);
kaddr = kmap_atomic(page);
kernel_read(file, kaddr, PAGE_SIZE, &offset);
flush_dcache_page(page);
kunmap_atomic(kaddr);
SetPageUptodate(page);
unlock_page(page);
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
static int qtfs_read_folio(struct file *file, struct folio *folio)
{
struct page *page = &folio->page;
qtfs_readpage(file, page);
return 0;
}
#endif
#ifndef KVER_4_19
static struct page **qtfs_alloc_pages(unsigned int nr)
{
struct page **pages = kzalloc(nr * (sizeof(struct page *)), GFP_KERNEL);
if (pages == NULL) {
qtfs_err("qtfs alloc pages failed.");
return NULL;
}
return pages;
}
static void qtfs_free_pages(struct page **pages)
{
kfree(pages);
}
static void qtfs_readahead(struct readahead_control *rac)
{
int i;
unsigned int nr_pages = readahead_count(rac);
struct page **pages = qtfs_alloc_pages(nr_pages);
qtfs_info("qtfs readahead.");
nr_pages = __readahead_batch(rac, pages, nr_pages);
for (i = 0; i < nr_pages; i++) {
qtfs_readpage(rac->file, pages[i]);
}
qtfs_free_pages(pages);
return;
}
#endif
static int qtfs_writepage(struct page *page, struct writeback_control *wbc)
{
qtfs_info("qtfs write page.");
return 0;
}
static int qtfs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
qtfs_info("qtfs write pages.");
return 0;
}
static ssize_t qtfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
qtfs_info("qtfs direct IO.");
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0))
static bool qtfs_dirty_folio(struct address_space *mapping, struct folio *folio)
{
qtfs_info("qtfs set page dirty.");
return filemap_dirty_folio(mapping, folio);
}
#else
static int qtfs_setpagedirty(struct page *page)
{
qtfs_info("qtfs set page dirty.");
__set_page_dirty_nobuffers(page);
return 0;
}
#endif
static const struct address_space_operations qtfs_aops = {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
.read_folio = qtfs_read_folio,
#else
.readpage = qtfs_readpage,
#endif
#ifndef KVER_4_19
.readahead = qtfs_readahead,
#endif
.writepage = qtfs_writepage,
.writepages = qtfs_writepages,
.direct_IO = qtfs_direct_IO,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0))
.dirty_folio = qtfs_dirty_folio,
#else
.set_page_dirty = qtfs_setpagedirty,
#endif
};
int qtfs_new_entry(struct inode *inode, struct dentry *dentry)
{
struct dentry *d = NULL;
if (!inode)
return -ENOMEM;
d_drop(dentry);
d = d_splice_alias(inode, dentry);
if (IS_ERR(d)) {
return PTR_ERR(d);
}
if (d) {
if (d->d_inode && S_ISDIR(d->d_inode->i_mode))
d->d_time = jiffies;
dput(d);
}
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_mkdir(struct mnt_idmap *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode)
#else
int qtfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_mkdir *req = NULL;
struct qtrsp_mkdir *rsp = NULL;
int ret;
struct inode *inode;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var.");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
req->mode = mode;
rsp = qtfs_remote_run(pvar, QTFS_REQ_MKDIR, QTFS_SEND_SIZE(struct qtreq_mkdir, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs mkdir failed %d.", rsp->errno);
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
inode = qtfs_iget(dentry->d_sb, &(rsp->inode_info));
ret = qtfs_new_entry(inode, dentry);
qtfs_info("mkdir path:%s success.", req->path);
qtfs_conn_put_param(pvar);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_create(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
#else
int qtfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_icreate *req;
struct qtrsp_icreate *rsp;
struct inode *inode;
int ret = 0;
int ret2 = 0;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var.");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
req->mode = mode;
req->excl = excl;
rsp = qtfs_remote_run(pvar, QTFS_REQ_ICREATE, QTFS_SEND_SIZE(struct qtreq_icreate, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
ret = rsp->errno;
qtfs_err("qtfs icreate failed %d.", rsp->errno);
qtfs_conn_put_param(pvar);
return ret;
}
ret = rsp->errno;
inode = qtfs_iget(dentry->d_sb, &(rsp->inode_info));
ret2 = qtfs_new_entry(inode, dentry);
qtfs_info("qtfs icreate get ret:%d, mode:%ho.", rsp->errno, rsp->inode_info.mode);
qtfs_conn_put_param(pvar);
return ret ? ret : ret2;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
#else
int qtfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_mknod *req;
struct qtrsp_mknod *rsp;
struct inode *inode;
int ret = 0;
int ret2 = 0;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
req->mode = mode;
req->dev = dev;
rsp = qtfs_remote_run(pvar, QTFS_REQ_MKNOD, sizeof(struct qtreq_mknod) - sizeof(req->path) + strlen(req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs mknod failed %d.", rsp->errno);
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
ret = rsp->errno;
qtfs_info("qtfs mknod success, path:<%s>.\n", req->path);
inode = qtfs_iget(dentry->d_sb, &(rsp->inode_info));
ret2 = qtfs_new_entry(inode, dentry);
qtfs_conn_put_param(pvar);
return ret ? ret : ret2;
}
static void qtfs_inode_priv_alloc(struct inode *inode)
{
struct qtfs_inode_priv *priv = kmem_cache_alloc(qtfs_inode_priv_cache, GFP_KERNEL);
if (priv == NULL) {
qtfs_err("qtfs inode priv alloc kmem cache alloc failed.");
return;
}
inode->i_private = priv;
priv->files = 0;
init_waitqueue_head(&priv->readq);
init_waitqueue_head(&priv->writeq);
return;
}
static void qtfs_init_inode(struct super_block *sb, struct inode *inode, struct inode_info *ii)
{
inode->i_sb = sb;
inode->i_mode = ii->mode;
inode->i_ino = ii->i_ino;
inode->i_size = ii->i_size;
inode->i_atime = ii->atime;
inode->i_mtime = ii->mtime;
inode->i_ctime = ii->ctime;
if (S_ISLNK(inode->i_mode)) {
if (is_sb_proc(sb)) {
qtfs_info("inode link ops set to qtfs_proc_sym_ops.");
inode->i_op = &qtfs_proc_sym_ops;
} else {
inode->i_op = &qtfs_symlink_inode_ops;
}
} else {
if (is_sb_proc(sb)) {
inode->i_op = &qtfs_proc_inode_ops;
} else {
inode->i_op = &qtfs_inode_ops;
}
}
inode->i_mapping->a_ops = &qtfs_aops;
if (S_ISDIR(ii->mode)) {
inode->i_fop = &qtfs_dir_ops;
} else if (S_ISREG(ii->mode)) {
inode->i_fop = &qtfs_file_ops;
} else if (S_ISFIFO(ii->mode)) {
inode->i_fop = &qtfsfifo_ops;
} else {
inode->i_fop = &qtfs_file_ops;
}
qtfs_inode_priv_alloc(inode);
return;
}
struct inode *qtfs_iget(struct super_block *sb, struct inode_info *ii)
{
struct inode *inode;
inode = new_inode(sb);
if (!inode)
return NULL;
qtfs_init_inode(sb, inode, ii);
return inode;
}
struct dentry *qtfs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_lookup *req;
struct qtrsp_lookup *rsp;
struct inode *inode;
struct dentry *d = NULL;
int ret;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var");
return NULL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
ret = qtfs_fullname(req->fullname, child_dentry, sizeof(req->fullname));
if (ret < 0) {
qtfs_err("qtfs lookup get fullname failed, too many path layers, <%s>!", req->fullname);
goto err_end;
}
rsp = qtfs_remote_run(pvar, QTFS_REQ_LOOKUP, strlen(req->fullname));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return (void *)rsp;
}
if (rsp->ret != QTFS_OK) {
qtfs_info("qtfs fs lookup failed, path:<%s> not exist at peer.\n", req->fullname);
d = ERR_PTR(rsp->errno);
qtfs_conn_put_param(pvar);
return d;
}
inode = qtfs_iget(parent_inode->i_sb, &(rsp->inode_info));
if (inode == NULL)
goto err_end;
d = d_splice_alias(inode, child_dentry);
qtfs_debug("qtfs lookup fullname:%s mode:%o(rsp:%o), ino:%lu(rsp:%lu).",
req->fullname, inode->i_mode, rsp->inode_info.mode, inode->i_ino, rsp->inode_info.i_ino);
if (d) {
if (d->d_inode && S_ISDIR(d->d_inode->i_mode))
d->d_time = jiffies;
}
qtfs_conn_put_param(pvar);
return d;
err_end:
qtfs_conn_put_param(pvar);
return NULL;
}
int qtfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_rmdir *req;
struct qtrsp_rmdir *rsp;
int ret;
struct inode *inode = d_inode(dentry);
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
rsp = qtfs_remote_run(pvar, QTFS_REQ_RMDIR, QTFS_SEND_SIZE(struct qtreq_rmdir, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs rmdir <%s> failed, errno:%d.\n", req->path, rsp->errno);
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
qtfs_info("qtfs rmdir success:<%s>.\n", req->path);
qtfs_conn_put_param(pvar);
if (inode->i_nlink > 0)
drop_nlink(inode);
d_invalidate(dentry);
return 0;
}
int qtfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct qtreq_unlink *req;
struct qtrsp_unlink *rsp;
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
int ret;
struct inode *inode = d_inode(dentry);
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
qtfs_info("qtfs unlink %s.\n", req->path);
rsp = qtfs_remote_run(pvar, QTFS_REQ_UNLINK, QTFS_SEND_SIZE(struct qtreq_unlink, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->errno < 0) {
qtfs_err("qtfs unlink %s failed, errno:%d\n", req->path, rsp->errno);
} else {
qtfs_info("qtfs unlink %s success\n", req->path);
inode->i_ctime = dir->i_ctime;
inode_dec_link_count(inode);
}
ret = rsp->errno;
qtfs_conn_put_param(pvar);
if (inode->i_nlink > 0)
drop_nlink(inode);
d_invalidate(dentry);
return ret;
}
int qtfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_link *req;
struct qtrsp_link *rsp;
int error;
struct inode *inode = d_inode(old_dentry);
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, old_dentry, sizeof(req->path));
req->d.oldlen = strlen(req->path) + 1;
QTFS_FULLNAME(req->path + req->d.oldlen, new_dentry, sizeof(req->path) - req->d.oldlen);
req->d.newlen = strlen(req->path + req->d.oldlen) + 1;
rsp = qtfs_remote_run(pvar, QTFS_REQ_LINK, sizeof(struct qtreq_link) - sizeof(req->path) + req->d.newlen + req->d.oldlen);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs link failed %d\n", rsp->errno);
error = rsp->errno;
goto err_end;
}
inode->i_ctime = current_time(inode);
inode_inc_link_count(inode);
ihold(inode);
d_instantiate(new_dentry, inode);
qtfs_info("qtfs link success, old:%s new:%s", req->path, req->path + req->d.oldlen);
qtfs_conn_put_param(pvar);
return 0;
err_end:
qtfs_conn_put_param(pvar);
return error;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *symname)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, const char *symname)
#else
int qtfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_symlink *req;
struct qtrsp_symlink *rsp;
struct inode *inode;
int error;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
req->d.newlen = strlen(req->path) + 1;
if (req->d.newlen + strlen(symname) + 1 > sizeof(req->path)) {
qtfs_conn_put_param(pvar);
qtfs_err("qtfs symlink path name too long\n");
return -EINVAL;
}
strlcpy(&req->path[req->d.newlen], symname, sizeof(req->path) - req->d.newlen - 1);
req->d.oldlen = strlen(&req->path[req->d.newlen]) + 1;
rsp = qtfs_remote_run(pvar, QTFS_REQ_SYMLINK, sizeof(struct qtreq_symlink) - sizeof(req->path) + req->d.newlen + req->d.oldlen);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs symlink failed %d\n", rsp->errno);
error = rsp->errno;
goto err_end;
}
inode = qtfs_iget(dentry->d_sb, &(rsp->inode_info));
error = qtfs_new_entry(inode, dentry);
qtfs_info("qtfs symlink success, path:%s symname:%s", req->path, symname);
qtfs_conn_put_param(pvar);
return error;
err_end:
qtfs_conn_put_param(pvar);
return error;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags)
#else
int qtfs_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_getattr *req;
struct qtrsp_getattr *rsp;
char *mnt_path = NULL;
struct inode *inode = path->dentry->d_inode;
int ret;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, path->dentry, sizeof(req->path));
req->request_mask = req_mask;
req->query_flags = flags;
mnt_path = qtfs_mountpoint_path_init(path->dentry, (struct path*)path, req->path);
if (IS_ERR(mnt_path)) {
qtfs_conn_put_param(pvar);
return PTR_ERR(mnt_path);
}
rsp = qtfs_remote_run(pvar, QTFS_REQ_GETATTR, QTFS_SEND_SIZE(struct qtreq_getattr, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret) {
qtfs_err("qtfs getattr <%s> failed.errno: %d %s\n", req->path, rsp->errno,
(rsp->errno != -ENOENT) ? "." : "file not exist");
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
*stat = rsp->stat;
if (path->dentry && path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
path->dentry->d_time = jiffies;
qtfs_debug("qtfs getattr success:<%s> blksiz:%u size:%lld mode:%o ino:%llu pathino:%lu. %s\n", req->path, rsp->stat.blksize,
rsp->stat.size, rsp->stat.mode, rsp->stat.ino, inode->i_ino, rsp->stat.ino != inode->i_ino ? "delete current inode" : "");
if (inode->i_ino != rsp->stat.ino || inode->i_mode != rsp->stat.mode) {
if (inode->i_nlink > 0){
drop_nlink(inode);
}
d_invalidate(path->dentry);
}
qtfs_conn_put_param(pvar);
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *attr)
#else
int qtfs_setattr(struct dentry *dentry, struct iattr *attr)
#endif
{
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
struct qtreq_setattr *req;
struct qtrsp_setattr *rsp;
int ret;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
QTFS_FULLNAME(req->path, dentry, sizeof(req->path));
req->attr = *attr;
req->attr.ia_file = NULL;
qtfs_info("iattr iavalid:%u mode:0x%o size:%lld\n",
req->attr.ia_valid, req->attr.ia_mode, req->attr.ia_size);
rsp = qtfs_remote_run(pvar, QTFS_REQ_SETATTR, QTFS_SEND_SIZE(struct qtreq_setattr, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs setattr <%s> failed. %d\n", req->path, rsp->errno);
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
qtfs_info("qtfs setattr <%s> success.\n", req->path);
qtfs_conn_put_param(pvar);
return 0;
}
const char *qtfs_getlink(struct dentry *dentry,
struct inode *inode, struct delayed_call *done)
{
struct qtfs_conn_var_s *pvar = NULL;
struct qtreq_getlink *req;
struct qtrsp_getlink *rsp;
size_t len = 0;
struct qtfs_fs_info *fsinfo = qtfs_priv_byinode(inode);
char *link = NULL;
link = READ_ONCE(inode->i_link);
if (link) {
qtfs_info("qtfs get link cache.\n");
return link;
}
if (dentry == NULL) {
return ERR_PTR(-ECHILD);
}
pvar = qtfs_conn_get_param();
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return ERR_PTR(-EINVAL);
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
if (qtfs_fullname(req->path, dentry, sizeof(req->path)) < 0) {
qtfs_err("qtfs fullname failed\n");
qtfs_conn_put_param(pvar);
return ERR_PTR(-EINVAL);
}
rsp = qtfs_remote_run(pvar, QTFS_REQ_GETLINK, QTFS_SEND_SIZE(struct qtreq_getlink, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return (void *)rsp;
}
if (rsp->ret == QTFS_ERR || strnlen(rsp->path, sizeof(rsp->path)) >= sizeof(rsp->path)) {
qtfs_err("qtfs getlink <%s> failed. %d\n", req->path, rsp->errno);
qtfs_conn_put_param(pvar);
return ERR_PTR(-ENOENT);
}
if (fsinfo->mnt_path)
len = strlen(fsinfo->mnt_path) + strlen(rsp->path) + 1;
else
len = strlen(rsp->path) + 1;
if (len > MAX_PATH_LEN || len == 0) {
qtfs_err("qtfs getlink failed. path name too long:%s - %s\n", fsinfo->mnt_path, rsp->path);
qtfs_conn_put_param(pvar);
return ERR_PTR(-EINVAL);
}
link = kmalloc(len, GFP_KERNEL);
if (!link) {
qtfs_conn_put_param(pvar);
return ERR_PTR(-ENOMEM);
}
memset(link, 0, len);
if (rsp->path[0] == '/' && fsinfo->mnt_path)
strcat(link, fsinfo->mnt_path);
strcat(link, rsp->path);
qtfs_info("get link success <%s>\n", link);
set_delayed_call(done, kfree_link, link);
qtfs_conn_put_param(pvar);
return link;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
int qtfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
int qtfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
#else
int qtfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
#endif
{
struct qtreq_rename *req;
struct qtrsp_rename *rsp;
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
int ret;
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return -EINVAL;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
req->d.oldlen = qtfs_fullname(req->path, old_dentry, sizeof(req->path));
if (req->d.oldlen < 0) {
qtfs_err("qtfs fullname failed\n");
qtfs_conn_put_param(pvar);
return -EINVAL;
}
req->d.oldlen += 1;
req->d.newlen = qtfs_fullname(&req->path[req->d.oldlen], new_dentry, sizeof(req->path) - req->d.oldlen);
if (req->d.newlen < 0) {
qtfs_err("qtfs fullname failed\n");
qtfs_conn_put_param(pvar);
return -EINVAL;
}
req->d.newlen += 1;
req->d.flags = flags;
rsp = qtfs_remote_run(pvar, QTFS_REQ_RENAME, sizeof(struct qtreq_rename) - sizeof(req->path) + req->d.oldlen + req->d.newlen);
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return QTFS_PTR_ERR(rsp);
}
if (rsp->ret == QTFS_ERR) {
qtfs_err("qtfs rename failed,errno:%d\n", rsp->errno);
} else {
qtfs_info("qtfs rename success, oldname:%s newname:%s flags:%x\n", req->path, &req->path[req->d.oldlen], flags);
}
ret = rsp->errno;
qtfs_conn_put_param(pvar);
return ret;
}
static struct inode_operations qtfs_inode_ops = {
.create = qtfs_create,
.lookup = qtfs_lookup,
.mkdir = qtfs_mkdir,
.rmdir = qtfs_rmdir,
.unlink = qtfs_unlink,
.symlink = qtfs_symlink,
.link = qtfs_link,
.mknod = qtfs_mknod,
.getattr = qtfs_getattr,
.setattr = qtfs_setattr,
.rename = qtfs_rename,
.listxattr = qtfs_xattr_list,
};
static struct inode_operations qtfs_symlink_inode_ops = {
.get_link = qtfs_getlink,
.getattr = qtfs_getattr,
.setattr = qtfs_setattr,
.listxattr = qtfs_xattr_list,
};
const struct xattr_handler *qtfs_xattr_handlers[] = {
&qtfs_xattr_user_handler,
&qtfs_xattr_trusted_handler,
&qtfs_xattr_security_handler,
#ifndef KVER_4_19
&qtfs_xattr_hurd_handler,
#endif
NULL
};
int qtfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
{
struct qtfs_conn_var_s *pvar = NULL;
struct qtreq_getattr *req;
struct qtrsp_getattr *rsp;
struct inode *inode = NULL;
if (dentry && dentry->d_inode) {
if (jiffies_to_msecs(jiffies - dentry->d_time) < 2000)
return 1;
pvar = qtfs_conn_get_param();
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return 0;
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
qtfs_fullname(req->path, dentry, PATH_MAX);
req->request_mask = STATX_BASIC_STATS;
req->query_flags = 0;
rsp = qtfs_remote_run(pvar, QTFS_REQ_GETATTR, QTFS_SEND_SIZE(struct qtreq_getattr, req->path));
if (IS_ERR_OR_NULL(rsp)) {
qtfs_conn_put_param(pvar);
return 0;
}
if (rsp->ret) {
qtfs_conn_put_param(pvar);
return 0;
}
inode = dentry->d_inode;
if (inode == NULL) {
qtfs_conn_put_param(pvar);
return 0;
}
if (inode->i_ino != rsp->stat.ino || inode->i_mode != rsp->stat.mode) {
if (inode->i_nlink > 0)
drop_nlink(inode);
qtfs_conn_put_param(pvar);
return 0;
}
qtfs_conn_put_param(pvar);
dentry->d_time = jiffies;
}
return 1;
}
const struct dentry_operations qtfs_dentry_ops = {
.d_revalidate = qtfs_dentry_revalidate,
};
static int qtfs_fill_super(struct super_block *sb, void *priv_data, int silent)
{
struct inode *root_inode;
int mode = S_IFDIR;
int err;
struct qtfs_fs_info *priv = (struct qtfs_fs_info *)priv_data;
root_inode = new_inode(sb);
root_inode->i_ino = 1;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
inode_init_owner(&nop_mnt_idmap, root_inode, NULL, mode);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
inode_init_owner(&init_user_ns, root_inode, NULL, mode);
#else
inode_init_owner(root_inode, NULL, mode);
#endif
root_inode->i_sb = sb;
if (priv->type == QTFS_PROC) {
qtfs_info("qtfs type: proc\n");
root_inode->i_op = &qtfs_proc_inode_ops;
} else {
qtfs_info("qtfs type: normal\n");
root_inode->i_op = &qtfs_inode_ops;
}
root_inode->i_fop = &qtfs_dir_ops;
root_inode->i_atime = root_inode->i_mtime = root_inode->i_ctime = CURRENT_TIME(root_inode);
sb->s_xattr = qtfs_xattr_handlers;
err = super_setup_bdi(sb);
if (err) {
qtfs_err("qtfs fill super bdi setup err:%d.\n", err);
}
sb->s_fs_info = priv;
sb->s_op = &qtfs_ops;
sb->s_time_gran = 1;
sb->s_d_op = &qtfs_dentry_ops;
sb->s_root = d_make_root(root_inode);
return 0;
}
struct dentry *qtfs_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
struct qtreq_mount *req = NULL;
struct qtrsp_mount *rsp = NULL;
struct dentry *ret;
struct qtfs_fs_info *priv = NULL;
int errno;
struct qtfs_conn_var_s *pvar = qtfs_conn_get_param();
if (!pvar) {
qtfs_err("Failed to get qtfs sock var\n");
return ERR_PTR(-ENXIO);
}
req = pvar->conn_ops->get_conn_msg_buf(pvar, QTFS_SEND);
strlcpy(req->path, dev_name, PATH_MAX);
rsp = qtfs_remote_run(pvar, QTFS_REQ_MOUNT, strlen(dev_name));
if (IS_ERR_OR_NULL(rsp) || rsp->ret != QTFS_OK) {
errno = IS_ERR_OR_NULL(rsp) ? -EFAULT : rsp->errno;
qtfs_err("qtfs fs mount failed, path:<%s> errno:%d.\n", dev_name, errno);
qtfs_conn_put_param(pvar);
return (IS_ERR_VALUE((long)errno)) ? ERR_PTR(errno) : ERR_PTR(-EFAULT);
}
priv = (struct qtfs_fs_info *)kmalloc(sizeof(struct qtfs_fs_info), GFP_KERNEL);
if (IS_ERR_OR_NULL(priv)) {
qtfs_err("qtfs priv kmalloc failed:%ld\n", QTFS_PTR_ERR(priv));
qtfs_conn_put_param(pvar);
return ERR_PTR(-ENOMEM);
}
memset(priv, 0, sizeof(struct qtfs_fs_info));
priv->type = qtfs_get_type((char *)data);
strlcpy(priv->peer_path, dev_name, NAME_MAX);
priv->mnt_path = NULL;
ret = mount_nodev(fs_type, flags, (void *)priv, qtfs_fill_super);
if (IS_ERR_OR_NULL(ret)) {
qtfs_err("mount qtfs error.\n");
} else {
qtfs_info("mount qtfs success dev name:%s.\n", dev_name);
}
qtfs_conn_put_param(pvar);
return ret;
}
void qtfs_kill_sb(struct super_block *sb)
{
struct qtfs_fs_info *fsinfo = sb->s_fs_info;
if (fsinfo->mnt_path) {
kfree(fsinfo->mnt_path);
fsinfo->mnt_path = NULL;
}
kfree(fsinfo);
sb->s_fs_info = NULL;
qtfs_info("qtfs superblock deleted.\n");
kill_anon_super(sb);
}