a68570b5d9
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>
1735 lines
54 KiB
C
1735 lines
54 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/fs.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/dirent.h>
|
|
#include <linux/xattr.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/statfs.h>
|
|
#include <linux/security.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/fs_struct.h>
|
|
#include <asm-generic/ioctls.h>
|
|
#include <asm-generic/termbits.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/file.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/version.h>
|
|
#include <linux/if_tun.h>
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
#include <linux/pipe_fs_i.h>
|
|
#include <linux/fdtable.h>
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0))
|
|
#include <linux/fdtable.h>
|
|
#endif
|
|
|
|
#include "conn.h"
|
|
#include "qtfs-server.h"
|
|
#include "req.h"
|
|
#include "log.h"
|
|
#include "fsops.h"
|
|
#include "comm.h"
|
|
#include "symbol_wrapper.h"
|
|
#include "qtfs_check.h"
|
|
|
|
#define REQ(arg) (arg->data)
|
|
#define RSP(arg) (arg->out)
|
|
#define USERP(arg) (arg->userp)
|
|
DEFINE_MUTEX(fd_bitmap_lock);
|
|
|
|
enum {
|
|
WHITELIST_MATCH_PREFIX = 0,
|
|
WHITELIST_MATCH_EXACT,
|
|
};
|
|
|
|
static inline int qtfs_white_list_match(char *path, char *wl, int wl_len, int match_type)
|
|
{
|
|
if (strncmp(path, wl, wl_len))
|
|
return 0;
|
|
switch (match_type) {
|
|
case WHITELIST_MATCH_PREFIX:
|
|
if (wl[wl_len - 1] != '/' && path[wl_len] != '\0' && path[wl_len] != '/')
|
|
return 0;
|
|
break;
|
|
case WHITELIST_MATCH_EXACT:
|
|
if (path[wl_len] != '\0')
|
|
return 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
// match success
|
|
return 1;
|
|
}
|
|
|
|
static bool _in_white_list(char *path, int type, int match_type)
|
|
{
|
|
int i, in_wl = -1;
|
|
char *str;
|
|
struct qtfs_wl_cap *cap;
|
|
|
|
str = strstr(path, "/..");
|
|
if (str != NULL && (str[3] == '\0' || str[3] =='/')) {
|
|
return false;
|
|
}
|
|
|
|
read_lock(&g_qtfs_wl.rwlock);
|
|
cap = &g_qtfs_wl.cap[type];
|
|
for (i = 0; i < cap->nums; i++) {
|
|
if (qtfs_white_list_match(path, cap->item[i], strlen(cap->item[i]), match_type)) {
|
|
in_wl = i;
|
|
break;
|
|
}
|
|
}
|
|
read_unlock(&g_qtfs_wl.rwlock);
|
|
return in_wl != -1;
|
|
}
|
|
|
|
static bool in_white_list(char *path, int type)
|
|
{
|
|
return _in_white_list(path, type, WHITELIST_MATCH_PREFIX);
|
|
}
|
|
|
|
static bool in_white_list_exact(char *path, int type)
|
|
{
|
|
return _in_white_list(path, type, WHITELIST_MATCH_EXACT);
|
|
}
|
|
|
|
static inline void qtfs_inode_info_fill(struct inode_info *ii, struct inode *inode)
|
|
{
|
|
ii->mode = inode->i_mode;
|
|
ii->i_opflags = inode->i_opflags;
|
|
ii->i_uid = inode->i_uid;
|
|
ii->i_gid = inode->i_gid;
|
|
ii->i_flags = inode->i_flags;
|
|
ii->i_ino = inode->i_ino;
|
|
ii->i_rdev = inode->i_rdev;
|
|
ii->i_size = inode->i_size;
|
|
ii->atime = inode->i_atime;
|
|
ii->mtime = inode->i_mtime;
|
|
ii->ctime = inode->i_ctime;
|
|
ii->i_bytes = inode->i_bytes;
|
|
ii->i_blkbits = inode->i_blkbits;
|
|
ii->i_write_hint = inode->i_write_hint;
|
|
ii->i_blocks = inode->i_blocks;
|
|
ii->i_state = inode->i_state;
|
|
ii->dirtied_when = inode->dirtied_when;
|
|
ii->dirtied_time_when = inode->dirtied_time_when;
|
|
ii->i_generation = inode->i_generation;
|
|
return;
|
|
}
|
|
|
|
#define QTFS_IOCTL_HANDLE_WITH_BREAK(rspsize)\
|
|
{\
|
|
ret = copy_from_user(rsp->buf, userp->userp, rspsize);\
|
|
if (ret) {\
|
|
qtfs_err("cmd:%d copy_from_user failed with:%d\n", req->d.cmd, ret);\
|
|
rsp->errno = -EFAULT;\
|
|
goto err;\
|
|
}\
|
|
rsp->size = rspsize;\
|
|
break;\
|
|
}
|
|
static int handle_ioctl(struct qtserver_arg *arg)
|
|
{
|
|
unsigned long ioctl_arg;
|
|
int ret;
|
|
int iret;
|
|
struct qtreq_ioctl *req = (struct qtreq_ioctl *)REQ(arg);
|
|
struct qtrsp_ioctl *rsp = (struct qtrsp_ioctl *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
mutex_lock(&fd_bitmap_lock);
|
|
if (req->d.fd < 0 || qtfs_fd_bitmap.bitmap == NULL || req->d.fd > qtfs_fd_bitmap.nbits || !test_bit(req->d.fd, qtfs_fd_bitmap.bitmap)) {
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
qtfs_err("ioctl invalid fd:%d", req->d.fd);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->size = 0;
|
|
rsp->errno = -EINVAL;
|
|
return sizeof(struct qtrsp_ioctl) - sizeof(rsp->buf);
|
|
}
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
|
|
if (req->d.argtype) {
|
|
ioctl_arg = req->d.arg;
|
|
} else {
|
|
if (req->d.size) {
|
|
if (req->d.size <= 0 || req->d.size > sizeof(req->path) || req->d.size >= userp->size) {
|
|
rsp->errno = -EINVAL;
|
|
goto err;
|
|
}
|
|
ret = copy_to_user(userp->userp, req->path, req->d.size);
|
|
if (ret) {
|
|
qtfs_err("cmd:%d copy_to_user failed with:%d", req->d.cmd, ret);
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
}
|
|
ioctl_arg = (unsigned long)userp->userp;
|
|
}
|
|
iret = qtfs_syscall_ioctl(req->d.fd, req->d.cmd, ioctl_arg);
|
|
if (iret) {
|
|
qtfs_err("ioctl fd:%d cmd:%d failed with %d", req->d.fd, req->d.cmd, iret);
|
|
rsp->errno = iret;
|
|
goto err;
|
|
}
|
|
qtfs_info("ioctl fd:%d cmd:%d argtype:%d arg:%lx size:%u successed", req->d.fd, req->d.cmd, req->d.argtype, req->d.arg, req->d.size);
|
|
switch (req->d.cmd) {
|
|
case TUNSETPERSIST:
|
|
case TUNSETIFF:
|
|
case TCSETS:
|
|
case FS_IOC_FSSETXATTR:
|
|
case SIOCADDMULTI:
|
|
case SIOCBRADDBR:
|
|
case SIOCBRDELBR:
|
|
case SIOCBRADDIF:
|
|
case SIOCBRDELIF:
|
|
case SIOCDELMULTI:
|
|
case SIOCDEVPRIVATE:
|
|
case SIOCETHTOOL:
|
|
case SIOCSIFFLAGS:
|
|
case SIOCSIFHWADDR:
|
|
case SIOCSIFMTU:
|
|
case SIOCSIFNAME:
|
|
rsp->size = 0;
|
|
break;
|
|
case FS_IOC_FSGETXATTR:
|
|
QTFS_IOCTL_HANDLE_WITH_BREAK(sizeof(struct fsxattr));
|
|
case TCGETS:
|
|
QTFS_IOCTL_HANDLE_WITH_BREAK(sizeof(struct ktermios));
|
|
case SIOCGIFHWADDR:
|
|
case SIOCGIFADDR:
|
|
case SIOCGIFFLAGS:
|
|
case SIOCGIFINDEX:
|
|
case SIOCGIFMTU:
|
|
case TUNGETIFF:
|
|
QTFS_IOCTL_HANDLE_WITH_BREAK(sizeof(struct ifreq));
|
|
case SIOCGIFVLAN:
|
|
QTFS_IOCTL_HANDLE_WITH_BREAK(sizeof(struct vlan_ioctl_args));
|
|
default:
|
|
rsp->errno = -EOPNOTSUPP;
|
|
goto err;
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
rsp->errno = iret;
|
|
return sizeof(struct qtrsp_ioctl) - sizeof(rsp->buf) + rsp->size;
|
|
err:
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->size = 0;
|
|
return sizeof(struct qtrsp_ioctl) - sizeof(rsp->buf);
|
|
}
|
|
|
|
static int handle_statfs(struct qtserver_arg *arg)
|
|
{
|
|
int ret;
|
|
struct qtreq_statfs *req = (struct qtreq_statfs *)REQ(arg);
|
|
struct qtrsp_statfs *rsp = (struct qtrsp_statfs *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
if (strlen(req->path) + 1 > userp->size) {
|
|
qtfs_err("invalid msg");
|
|
rsp->errno = -EINVAL;
|
|
goto err_end;
|
|
}
|
|
ret = copy_to_user(userp->userp, req->path, strlen(req->path) + 1);
|
|
if (ret) {
|
|
rsp->errno = -EFAULT;
|
|
goto err_end;
|
|
}
|
|
|
|
ret = qtfs_syscall_statfs((char *)userp->userp, userp->userp2);
|
|
if (ret) {
|
|
qtfs_err("qtfs server handle statfs path:%s failed with ret:%d.\n", req->path, ret);
|
|
rsp->errno = ret;
|
|
goto err_end;
|
|
} else {
|
|
qtfs_info("qtfs server handle statfs path:%s success.\n", req->path);
|
|
rsp->ret = QTFS_OK;
|
|
}
|
|
if (copy_from_user(&rsp->kstat, userp->userp2, sizeof(struct kstatfs))) {
|
|
qtfs_err("copy statfs to kstatfs failed");
|
|
rsp->errno = -EFAULT;
|
|
goto err_end;
|
|
}
|
|
return sizeof(struct qtrsp_statfs);
|
|
err_end:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_statfs);
|
|
}
|
|
|
|
static int handle_mount(struct qtserver_arg *arg)
|
|
{
|
|
struct path path;
|
|
int ret;
|
|
struct qtreq_mount *req = (struct qtreq_mount *)REQ(arg);
|
|
struct qtrsp_mount *rsp = (struct qtrsp_mount *)RSP(arg);
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_MOUNT)) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -EPERM;
|
|
return sizeof(rsp);
|
|
}
|
|
|
|
ret = kern_path(req->path, LOOKUP_DIRECTORY, &path);
|
|
if (ret) {
|
|
qtfs_err("handle mount path:%s kern_path failed, ret: %d.\n", req->path, ret);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -EINVAL;
|
|
} else {
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle mount path:%s success.\n", req->path);
|
|
path_put(&path);
|
|
}
|
|
return sizeof(rsp);
|
|
}
|
|
|
|
int handle_open(struct qtserver_arg *arg)
|
|
{
|
|
int fd;
|
|
int ret;
|
|
struct qtreq_open *req = (struct qtreq_open *)REQ(arg);
|
|
struct qtrsp_open *rsp = (struct qtrsp_open *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_OPEN) || qtfs_fd_bitmap.bitmap == NULL) {
|
|
qtfs_err("handle open path:%s not permited", req->path);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->fd = -EACCES;
|
|
return sizeof(struct qtrsp_open);
|
|
}
|
|
if (strlen(req->path) + 1 > userp->size) {
|
|
qtfs_err("path len invalid.");
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->fd = -EFAULT;
|
|
return sizeof(struct qtrsp_open);
|
|
}
|
|
|
|
ret = copy_to_user(userp->userp, req->path, strlen(req->path)+1);
|
|
if (ret) {
|
|
qtfs_err("handle open copy to user failed, ret:%d path:%s", ret, req->path);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->fd = -EFAULT;
|
|
return sizeof(struct qtrsp_open);
|
|
}
|
|
fd = qtfs_syscall_openat(AT_FDCWD, (char *)userp->userp, req->flags, req->mode);
|
|
if (fd == -EEXIST) {
|
|
qtfs_err("handle open file <<%s>> flags:%llx mode:%o, opened:failed %d, do again\n", req->path, req->flags, req->mode, fd);
|
|
req->flags &= ~(O_CREAT | O_EXCL);
|
|
fd = qtfs_syscall_openat(AT_FDCWD, (char *)userp->userp, req->flags, req->mode);
|
|
}
|
|
if (fd < 0 || fd > qtfs_fd_bitmap.nbits) {
|
|
if (fd != -ENOENT) {
|
|
qtfs_err("handle open file <<%s>>flags:%llx mode:%o, opened:failed %d\n", req->path, req->flags, req->mode, fd);
|
|
} else {
|
|
qtfs_info("handle open file <<%s>>flags:%llx mode:%o, opened:failed - file not exist\n", req->path, req->flags, req->mode);
|
|
}
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->fd = (fd < 0) ? fd : -EINVAL;
|
|
return sizeof(struct qtrsp_open);
|
|
}
|
|
mutex_lock(&fd_bitmap_lock);
|
|
__set_bit(fd, qtfs_fd_bitmap.bitmap);
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
rsp->ret = QTFS_OK;
|
|
rsp->fd = fd;
|
|
return sizeof(struct qtrsp_open);
|
|
}
|
|
|
|
int handle_close(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_close *req = (struct qtreq_close *)REQ(arg);
|
|
struct qtrsp_close *rsp = (struct qtrsp_close *)RSP(arg);
|
|
|
|
// fd >= 3 is valid
|
|
if (req->fd <= 2) {
|
|
qtfs_err("handle close an invalid fd:%d.", req->fd);
|
|
WARN_ON(1);
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_close);
|
|
}
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0))
|
|
rsp->ret = qtfs_kern_syms.__close_fd(current->files, req->fd);
|
|
#else
|
|
rsp->ret = close_fd(req->fd);
|
|
#endif
|
|
mutex_lock(&fd_bitmap_lock);
|
|
if (req->fd > qtfs_fd_bitmap.nbits || qtfs_fd_bitmap.bitmap == NULL || !test_bit(req->fd, qtfs_fd_bitmap.bitmap)) {
|
|
qtfs_err("close fd:%d bitmap is notset", req->fd);
|
|
} else {
|
|
__clear_bit(req->fd, qtfs_fd_bitmap.bitmap);
|
|
}
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
qtfs_info("handle close file, fd:%d ret:%d", req->fd, rsp->ret);
|
|
return sizeof(struct qtrsp_close);
|
|
}
|
|
|
|
static int handle_readiter(struct qtserver_arg *arg)
|
|
{
|
|
struct file *file = NULL;
|
|
char *pathbuf, *fullname;
|
|
int idx = 0;
|
|
int ret = 0;
|
|
int block_size;
|
|
size_t maxlen;
|
|
int len;
|
|
off_t seek;
|
|
struct qtreq_readiter *req = (struct qtreq_readiter *)REQ(arg);
|
|
struct qtrsp_readiter *rsp = (struct qtrsp_readiter *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
mutex_lock(&fd_bitmap_lock);
|
|
if (req->fd < 0 || qtfs_fd_bitmap.bitmap == NULL || req->fd > qtfs_fd_bitmap.nbits || !test_bit(req->fd, qtfs_fd_bitmap.bitmap)) {
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
qtfs_err("unset bitmap fd:%d is error request, fd limit:%u", req->fd, qtfs_fd_bitmap.nbits);
|
|
rsp->d.errno = -EINVAL;
|
|
goto early_end;
|
|
}
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
file = fget(req->fd);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
qtfs_err("handle readiter error, open failed.\n");
|
|
rsp->d.errno = -ENOENT;
|
|
goto early_end;
|
|
}
|
|
if (file->f_flags & O_DIRECT) {
|
|
if (file->f_inode->i_sb->s_bdev != NULL && file->f_inode->i_sb->s_bdev->bd_disk != NULL
|
|
&& file->f_inode->i_sb->s_bdev->bd_disk->queue != NULL) {
|
|
block_size = bdev_logical_block_size(file->f_inode->i_sb->s_bdev);
|
|
} else {
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.errno = -EINVAL;
|
|
rsp->d.len = 0;
|
|
goto end;
|
|
}
|
|
if (req->len % block_size != 0) {
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.errno = -EINVAL;
|
|
rsp->d.len = 0;
|
|
goto end;
|
|
}
|
|
maxlen = (req->len >= sizeof(rsp->readbuf)) ? (block_size * (sizeof(rsp->readbuf) /block_size)) : req->len;
|
|
} else {
|
|
maxlen = (req->len >= sizeof(rsp->readbuf)) ? (sizeof(rsp->readbuf) - 1) : req->len;
|
|
}
|
|
|
|
pathbuf = __getname();
|
|
if (pathbuf == NULL) {
|
|
qtfs_err("readiter whitelist judge error: failed to get pathbuf.\n");
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.errno = -ENOENT;
|
|
goto end;
|
|
}
|
|
fullname = file_path(file, pathbuf, PATH_MAX);
|
|
if (IS_ERR_OR_NULL(fullname) || !in_white_list(fullname, QTFS_WHITELIST_READ)) {
|
|
qtfs_err("read iter path not in whitelist.\n");
|
|
__putname(pathbuf);
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.errno = -ENOENT;
|
|
goto end;
|
|
}
|
|
qtfs_info("handle readiter file:<%s> len:%lu pos:%lld", fullname, req->len, req->pos);
|
|
__putname(pathbuf);
|
|
|
|
if (file->f_mode & FMODE_LSEEK) {
|
|
seek = qtfs_syscall_lseek(req->fd, req->pos, SEEK_SET);
|
|
if (seek < 0) {
|
|
qtfs_err("handle read set lseek pos:%lld failed, fd:%d ret:%ld", req->pos, req->fd, seek);
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.errno = seek;
|
|
fput(file);
|
|
return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf);
|
|
}
|
|
}
|
|
fput(file);
|
|
|
|
rsp->d.ret = QTFS_OK;
|
|
while (maxlen > 0) {
|
|
len = (maxlen > userp->size) ? userp->size : maxlen;
|
|
ret = qtfs_syscall_read(req->fd, userp->userp, len);
|
|
if (ret < 0) {
|
|
qtfs_err("read fd:%d failed:%d", req->fd, ret);
|
|
rsp->d.ret = QTFS_ERR;
|
|
break;
|
|
}
|
|
if (ret == 0) {
|
|
rsp->d.end = 1;
|
|
break;
|
|
}
|
|
maxlen -= len;
|
|
if (copy_from_user(&rsp->readbuf[idx], userp->userp, ret)) {
|
|
qtfs_err("copy from user failed fd:%d len:%d", req->fd, ret);
|
|
rsp->d.end = 1;
|
|
rsp->d.ret = QTFS_ERR;
|
|
break;
|
|
}
|
|
idx += ret;
|
|
rsp->d.len += ret;
|
|
if (ret < len) {
|
|
rsp->d.end = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
qtfs_info("read fd:%d len:%ld %s errno:%d", req->fd, rsp->d.len,
|
|
(rsp->d.ret == QTFS_OK) ? "Successed" : "Failed", rsp->d.errno);
|
|
|
|
return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf) + ((rsp->d.len < 0) ? 0 : rsp->d.len);
|
|
end:
|
|
fput(file);
|
|
return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf) + ((rsp->d.len < 0) ? 0 : rsp->d.len);
|
|
early_end:
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.len = 0;
|
|
return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf);
|
|
}
|
|
|
|
static int handle_write(struct qtserver_arg *arg)
|
|
{
|
|
struct file *file = NULL;
|
|
char *pathbuf, *fullname;
|
|
int block_size;
|
|
struct qtreq_write *req = (struct qtreq_write *)REQ(arg);
|
|
struct qtrsp_write *rsp = (struct qtrsp_write *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
int idx = 0, leftlen = 0, ret = 0, len = 0;
|
|
off_t seek;
|
|
|
|
mutex_lock(&fd_bitmap_lock);
|
|
if (req->d.fd < 0 || qtfs_fd_bitmap.bitmap == NULL || req->d.fd > qtfs_fd_bitmap.nbits || !test_bit(req->d.fd, qtfs_fd_bitmap.bitmap)) {
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
qtfs_err("unset bitmap fd:%d is error request, fd limit:%u", req->d.fd, qtfs_fd_bitmap.nbits);
|
|
goto early_end;
|
|
}
|
|
mutex_unlock(&fd_bitmap_lock);
|
|
file = fget(req->d.fd);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
qtfs_err("qtfs handle write error, open failed.\n");
|
|
goto early_end;
|
|
}
|
|
if (file->f_flags & O_DIRECT) {
|
|
if (file->f_inode->i_sb->s_bdev != NULL && file->f_inode->i_sb->s_bdev->bd_disk != NULL
|
|
&& file->f_inode->i_sb->s_bdev->bd_disk->queue != NULL) {
|
|
block_size = bdev_logical_block_size(file->f_inode->i_sb->s_bdev);
|
|
} else {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = -EINVAL;
|
|
goto end;
|
|
}
|
|
if (req->d.total_len % block_size != 0) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = -EINVAL;
|
|
goto end;
|
|
}
|
|
leftlen = block_size * (req->d.buflen / block_size);
|
|
} else {
|
|
leftlen = req->d.buflen;
|
|
}
|
|
if (leftlen < 0 || leftlen > sizeof(req->path_buf)) {
|
|
qtfs_err("invalid buflen :%d", leftlen);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = -EINVAL;
|
|
goto end;
|
|
}
|
|
pathbuf = __getname();
|
|
if (pathbuf == NULL) {
|
|
qtfs_err("write whitelist judge error: failed to get pathbuf.\n");
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
fullname = file_path(file, pathbuf, PATH_MAX);
|
|
if (IS_ERR_OR_NULL(fullname) || !in_white_list(fullname, QTFS_WHITELIST_WRITE)) {
|
|
__putname(pathbuf);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
qtfs_info("handle write fd:%d file:<%s>, write len:%d before pos:%lld mode:%o flags:%x", req->d.fd, fullname,
|
|
leftlen, req->d.pos, file->f_mode, file->f_flags);
|
|
__putname(pathbuf);
|
|
|
|
if (file->f_mode & FMODE_LSEEK) {
|
|
seek = qtfs_syscall_lseek(req->d.fd, req->d.pos, SEEK_SET);
|
|
if (seek < 0) {
|
|
qtfs_err("handle write set lseek pos:%lld failed, fd:%d ret:%ld", req->d.pos, req->d.fd, seek);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = seek;
|
|
fput(file);
|
|
return sizeof(struct qtrsp_write);
|
|
}
|
|
}
|
|
fput(file);
|
|
|
|
rsp->ret = QTFS_OK;
|
|
while (leftlen > 0) {
|
|
len = (leftlen > userp->size) ? userp->size : leftlen;
|
|
leftlen -= len;
|
|
if (copy_to_user(userp->userp, &req->path_buf[idx], len)) {
|
|
qtfs_err("copy to user failed len:%d idx:%d", len, idx);
|
|
rsp->ret = QTFS_ERR;
|
|
break;
|
|
}
|
|
idx += len;
|
|
ret = qtfs_syscall_write(req->d.fd, userp->userp, len);
|
|
if (ret <= 0) {
|
|
qtfs_err("write failed ret:%d", ret);
|
|
rsp->ret = QTFS_ERR;
|
|
break;
|
|
}
|
|
rsp->len += ret;
|
|
}
|
|
|
|
qtfs_info("write fd:%d len:%ld %s", req->d.fd, rsp->len, (rsp->ret == QTFS_OK) ? "Successed" : "Failed");
|
|
return sizeof(struct qtrsp_write);
|
|
end:
|
|
fput(file);
|
|
return sizeof(struct qtrsp_write);
|
|
early_end:
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->len = 0;
|
|
return sizeof(struct qtrsp_write);
|
|
}
|
|
|
|
static int handle_lookup(struct qtserver_arg *arg)
|
|
{
|
|
struct path path;
|
|
struct inode *inode;
|
|
struct qtreq_lookup *req = (struct qtreq_lookup *)REQ(arg);
|
|
struct qtrsp_lookup *rsp = (struct qtrsp_lookup *)RSP(arg);
|
|
int ret;
|
|
ret = kern_path(req->fullname, 0, &path);
|
|
if (ret) {
|
|
qtfs_info("qtfs handle lookup(%s) kern_path failed, ret%d.\n", req->fullname, ret);
|
|
rsp->errno = (ret == -ENOENT ? 0 : ret);
|
|
rsp->ret = QTFS_ERR;
|
|
} else {
|
|
inode = path.dentry->d_inode;
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_inode_info_fill(&rsp->inode_info, inode);
|
|
qtfs_debug("handle lookup name:%s, mode:%o ino:%lu", req->fullname, rsp->inode_info.mode, rsp->inode_info.i_ino);
|
|
path_put(&path);
|
|
}
|
|
return sizeof(struct qtrsp_lookup);
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
|
|
static bool qtfs_filldir(struct dir_context *ctx, const char *name, int namelen,
|
|
loff_t offset, u64 ino, unsigned int d_type)
|
|
#else
|
|
static int qtfs_filldir(struct dir_context *ctx, const char *name, int namelen,
|
|
loff_t offset, u64 ino, unsigned int d_type)
|
|
#endif
|
|
{
|
|
struct qtfs_dirent64 *dirent, *prev;
|
|
struct qtfs_getdents *buf = container_of(ctx, struct qtfs_getdents, ctx);
|
|
int reclen = ALIGN(offsetof(struct qtfs_dirent64, d_name) + namelen + 1, sizeof(u64));
|
|
int prev_reclen;
|
|
|
|
if (reclen > buf->count || !buf->dir || !name)
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
|
|
return false;
|
|
#else
|
|
return -EINVAL;
|
|
#endif
|
|
|
|
prev_reclen = buf->prev_reclen;
|
|
dirent = buf->dir;
|
|
prev = (void *)dirent - prev_reclen;
|
|
prev->d_off = offset;
|
|
dirent->d_ino = ino;
|
|
dirent->d_reclen = reclen;
|
|
dirent->d_type = d_type;
|
|
memcpy(dirent->d_name, name, namelen);
|
|
|
|
buf->prev_reclen = reclen;
|
|
buf->dir = (void *)dirent + reclen;
|
|
buf->count -= reclen;
|
|
buf->vldcnt++;
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
|
|
return true;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int handle_readdir(struct qtserver_arg *arg)
|
|
{
|
|
struct file *file = NULL;
|
|
struct qtreq_readdir *req = (struct qtreq_readdir *)REQ(arg);
|
|
struct qtrsp_readdir *rsp = (struct qtrsp_readdir *)RSP(arg);
|
|
int ret;
|
|
struct qtfs_getdents buf = {
|
|
.ctx.actor = qtfs_filldir,
|
|
.ctx.pos = req->pos,
|
|
.prev_reclen = 0,
|
|
.count = req->count,
|
|
.dir = (struct qtfs_dirent64 *)rsp->dirent,
|
|
.vldcnt = 0,
|
|
};
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_READDIR)) {
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.vldcnt = 0;
|
|
return sizeof(struct qtrsp_readdir) - sizeof(rsp->dirent);
|
|
}
|
|
file = filp_open(req->path, O_RDONLY|O_NONBLOCK|O_DIRECTORY, 0);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
qtfs_err("handle readdir error, filp:<%s> open failed.\n", req->path);
|
|
rsp->d.ret = QTFS_ERR;
|
|
rsp->d.vldcnt = 0;
|
|
return sizeof(struct qtrsp_readdir) - sizeof(rsp->dirent);
|
|
}
|
|
file->f_pos = req->pos;
|
|
ret = iterate_dir(file, &buf.ctx);
|
|
rsp->d.pos = file->f_pos;
|
|
rsp->d.ret = QTFS_OK;
|
|
rsp->d.vldcnt = buf.vldcnt;
|
|
rsp->d.over = (req->pos == rsp->d.pos) ? 1 : 0;
|
|
qtfs_info("handle readdir ret:%d, pos:%lld path:%s, valid count:%d, leftcount:%d validbyte:%lu\n",
|
|
ret, req->pos, req->path, buf.vldcnt, buf.count, sizeof(rsp->dirent) - buf.count);
|
|
filp_close(file, NULL);
|
|
|
|
return sizeof(struct qtrsp_readdir) - buf.count;
|
|
}
|
|
|
|
static int handle_mkdir(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_mkdir *req = (struct qtreq_mkdir *)REQ(arg);
|
|
struct qtrsp_mkdir *rsp = (struct qtrsp_mkdir *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
struct inode *inode;
|
|
struct path path;
|
|
int ret;
|
|
int len;
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_MKDIR)) {
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
len = strlen(req->path);
|
|
if (len < 0 || len + 1 > userp->size || len >= sizeof(req->path)) {
|
|
qtfs_err("path len invalid:%d.", len);
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
|
|
}
|
|
if (copy_to_user(userp->userp, req->path, len + 1)) {
|
|
qtfs_err("handle mkdir copy to userp failed.\n");
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
rsp->errno = qtfs_syscall_mkdirat(AT_FDCWD, userp->userp, req->mode);
|
|
if (rsp->errno < 0) {
|
|
qtfs_err("handle mkdir path:%s failed with ret:%d.", req->path, rsp->errno);
|
|
goto err;
|
|
}
|
|
ret = kern_path(req->path, 0, &path);
|
|
if (ret) {
|
|
qtfs_err("handle mkdir failed in kern path, ret:%d.\n", ret);
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
} else {
|
|
inode = d_inode(path.dentry);
|
|
qtfs_inode_info_fill(&rsp->inode_info, inode);
|
|
path_put(&path);
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle mkdir path:%s success.", req->path);
|
|
return sizeof(struct qtrsp_mkdir);
|
|
|
|
err:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_mkdir);
|
|
}
|
|
|
|
static int handle_rmdir(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_rmdir *req = (struct qtreq_rmdir *)REQ(arg);
|
|
struct qtrsp_rmdir *rsp = (struct qtrsp_rmdir *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
int len;
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_RMDIR)) {
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
len = strlen(req->path);
|
|
if (len < 0 || len + 1 > userp->size || len >= sizeof(req->path)) {
|
|
qtfs_err("len invalid:%d", len);
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
if (copy_to_user(userp->userp, req->path, len + 1)) {
|
|
qtfs_err("handle rmdir copy to userp failed.\n");
|
|
rsp->errno = -EFAULT;
|
|
goto err;
|
|
}
|
|
rsp->errno = qtfs_syscall_rmdir(userp->userp);
|
|
if (rsp->errno < 0) {
|
|
qtfs_err("handle rmdir error:%d.", rsp->errno);
|
|
goto err;
|
|
}
|
|
qtfs_info("handle rmdir path:%s success.", req->path);
|
|
rsp->ret = QTFS_OK;
|
|
return sizeof(struct qtrsp_rmdir);
|
|
|
|
err:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_rmdir);
|
|
}
|
|
|
|
static int handle_getattr(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_getattr *req = (struct qtreq_getattr *)REQ(arg);
|
|
struct qtrsp_getattr *rsp = (struct qtrsp_getattr *)RSP(arg);
|
|
struct path path;
|
|
int ret;
|
|
|
|
qtfs_debug("handle getattr path:%s\n", req->path);
|
|
ret = kern_path(req->path, 0, &path);
|
|
if (ret) {
|
|
rsp->errno = ret;
|
|
qtfs_err("handle getattr path:%s failed, ret:%d %s\n", req->path, ret, (ret != -ENOENT) ? "." : "file not exist");
|
|
goto failed;
|
|
}
|
|
|
|
ret = vfs_getattr(&path, &rsp->stat, req->request_mask, req->query_flags);
|
|
if (ret) {
|
|
qtfs_err("vfs getattr path:%s ret:%d\n", req->path, ret);
|
|
rsp->errno = ret;
|
|
path_put(&path);
|
|
goto failed;
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
path_put(&path);
|
|
qtfs_debug("handle getattr:<%s> blksize:%u size:%lld mode:%o ino:%llu req_mask:%x req_flags:%u.\n", req->path, rsp->stat.blksize,
|
|
rsp->stat.size, rsp->stat.mode, rsp->stat.ino, req->request_mask, req->query_flags);
|
|
return sizeof(struct qtrsp_getattr);
|
|
|
|
failed:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_getattr);
|
|
}
|
|
|
|
static int handle_setattr(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_setattr *req = (struct qtreq_setattr *)REQ(arg);
|
|
struct qtrsp_setattr *rsp = (struct qtrsp_setattr *)RSP(arg);
|
|
struct inode *inode = NULL;
|
|
struct path path;
|
|
int ret;
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_SETATTR)) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -ENOENT;
|
|
return sizeof(struct qtrsp_setattr);
|
|
}
|
|
|
|
ret = kern_path(req->path, 0, &path);
|
|
if (ret) {
|
|
qtfs_err("handle setattr path:%s failed in kern_path with %d\n", req->path, ret);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -ENOENT;
|
|
return sizeof(struct qtrsp_setattr);
|
|
}
|
|
inode = path.dentry->d_inode;
|
|
if (req->attr.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) {
|
|
req->attr.ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID |
|
|
ATTR_MODE);
|
|
req->attr.ia_mode = inode->i_mode;
|
|
if (inode->i_mode & S_ISUID) {
|
|
req->attr.ia_valid |= ATTR_MODE;
|
|
req->attr.ia_mode &= ~S_ISUID;
|
|
}
|
|
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
|
|
req->attr.ia_valid |= ATTR_MODE;
|
|
req->attr.ia_mode &= ~S_ISGID;
|
|
}
|
|
}
|
|
if (!req->attr.ia_valid) {
|
|
rsp->ret = QTFS_OK;
|
|
path_put(&path);
|
|
return sizeof(struct qtrsp_setattr);
|
|
}
|
|
|
|
inode_lock(inode);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
rsp->errno = notify_change(&nop_mnt_idmap, path.dentry, &req->attr, NULL);
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
rsp->errno = notify_change(&init_user_ns, path.dentry, &req->attr, NULL);
|
|
#else
|
|
rsp->errno = notify_change(path.dentry, &req->attr, NULL);
|
|
#endif
|
|
if (rsp->errno < 0) {
|
|
rsp->ret = QTFS_ERR;
|
|
qtfs_err("handle setattr, path:<%s> failed with %d.\n", req->path, ret);
|
|
goto end;
|
|
}
|
|
|
|
qtfs_info("handle setattr iattr success iavalid:%u mode:%o size:%lld\n",
|
|
req->attr.ia_valid, req->attr.ia_mode, req->attr.ia_size);
|
|
rsp->ret = QTFS_OK;
|
|
|
|
end:
|
|
inode_unlock(inode);
|
|
path_put(&path);
|
|
return sizeof(struct qtrsp_setattr);
|
|
}
|
|
|
|
int handle_icreate(struct qtserver_arg *arg)
|
|
{
|
|
struct file *file = NULL;
|
|
struct inode *inode;
|
|
struct qtreq_icreate *req = (struct qtreq_icreate *)REQ(arg);
|
|
struct qtrsp_icreate *rsp = (struct qtrsp_icreate *)RSP(arg);
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_CREATE)) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -ENOENT;
|
|
return sizeof(struct qtrsp_icreate);
|
|
}
|
|
|
|
file = filp_open(req->path, O_CREAT, req->mode);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
qtfs_err("handle icreate filp:<%s> failed in open.\n", req->path);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = QTFS_PTR_ERR(file);
|
|
return sizeof(struct qtrsp_icreate);
|
|
}
|
|
inode = file->f_inode;
|
|
qtfs_inode_info_fill(&rsp->inode_info, inode);
|
|
filp_close(file, NULL);
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle icreate path:%s success, inode mode:%ho\n", req->path,
|
|
rsp->inode_info.mode);
|
|
return sizeof(struct qtrsp_icreate);
|
|
}
|
|
|
|
static int handle_mknod(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_mknod *req = (struct qtreq_mknod *)REQ(arg);
|
|
struct qtrsp_mknod *rsp = (struct qtrsp_mknod *)RSP(arg);
|
|
struct dentry *dent = NULL;
|
|
struct path path;
|
|
int error;
|
|
unsigned int flags = LOOKUP_DIRECTORY;
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_CREATE)) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = -ENOENT;
|
|
return sizeof(struct qtrsp_mknod);
|
|
}
|
|
|
|
retry:
|
|
dent = kern_path_create(AT_FDCWD, req->path, &path, flags);
|
|
if (IS_ERR_OR_NULL(dent)) {
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->errno = QTFS_PTR_ERR(dent);
|
|
qtfs_info("handle mknod path:<%s>, mode:%o in kern_path_create with ret:%ld\n", req->path, req->mode, QTFS_PTR_ERR(dent));
|
|
return sizeof(struct qtrsp_mknod);
|
|
}
|
|
|
|
if (!IS_POSIXACL(path.dentry->d_inode))
|
|
req->mode &= ~current_umask();
|
|
error = security_path_mknod(&path, dent, req->mode, req->dev);
|
|
if (!error)
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
error = vfs_mknod(&nop_mnt_idmap, path.dentry->d_inode, dent, req->mode, req->dev);
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
error = vfs_mknod(&init_user_ns, path.dentry->d_inode, dent, req->mode, req->dev);
|
|
#else
|
|
error = vfs_mknod(path.dentry->d_inode, dent, req->mode, req->dev);
|
|
#endif
|
|
done_path_create(&path, dent);
|
|
if (error == -ESTALE && !(flags & LOOKUP_REVAL)) {
|
|
flags |= LOOKUP_REVAL;
|
|
qtfs_debug("retry mknod.\n");
|
|
rsp->errno = error;
|
|
goto retry;
|
|
}
|
|
qtfs_inode_info_fill(&rsp->inode_info, dent->d_inode);
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle mknod path:<%s>, mode:%o success\n", req->path, req->mode);
|
|
rsp->errno = 0;
|
|
return sizeof(struct qtrsp_mknod);
|
|
}
|
|
|
|
int handle_unlink(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_unlink *req = (struct qtreq_unlink *)REQ(arg);
|
|
struct qtrsp_unlink *rsp = (struct qtrsp_unlink *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
int len;
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_UNLINK)) {
|
|
rsp->errno = -ENOENT;
|
|
return sizeof(struct qtrsp_unlink);
|
|
}
|
|
len = strlen(req->path);
|
|
if (len < 0 || len + 1 > userp->size || len >= sizeof(req->path)) {
|
|
qtfs_err("len invalid:%d", len);
|
|
rsp->errno = -EFAULT;
|
|
return sizeof(struct qtrsp_unlink);
|
|
}
|
|
if (copy_to_user(userp->userp, req->path, len + 1)) {
|
|
qtfs_err("handle unlink copy to userp failed.");
|
|
rsp->errno = -EFAULT;
|
|
return sizeof(struct qtrsp_unlink);
|
|
}
|
|
|
|
rsp->errno = qtfs_syscall_unlink(userp->userp);
|
|
if (rsp->errno < 0) {
|
|
qtfs_err("handle unlink failed, errno:%d\n", rsp->errno);
|
|
} else {
|
|
qtfs_info("handle unlink path:%s success\n", req->path);
|
|
}
|
|
return sizeof(struct qtrsp_unlink);
|
|
}
|
|
|
|
int handle_link(struct qtserver_arg *arg)
|
|
{
|
|
char *oldname, *newname;
|
|
struct qtreq_link *req = (struct qtreq_link *)REQ(arg);
|
|
struct qtrsp_link *rsp = (struct qtrsp_link *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
oldname = req->path;
|
|
newname = req->path + req->d.oldlen;
|
|
if (strlen(oldname) >= sizeof(req->path) || req->d.oldlen >= sizeof(req->path) ||
|
|
strlen(oldname) + strlen(newname) >= sizeof(req->path) ||
|
|
strlen(oldname) < 0 || strlen(newname) < 0) {
|
|
qtfs_err("invalid path oldname or newname during handle_link");
|
|
rsp->errno = -EFAULT;
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_link);
|
|
}
|
|
if (strlen(oldname) + 1 > userp->size || strlen(newname) + 1 > userp->size ||
|
|
copy_to_user(userp->userp, oldname, strlen(oldname) + 1) ||
|
|
copy_to_user(userp->userp2, newname, strlen(newname) + 1)) {
|
|
qtfs_err("handle link failed in copy to userp.\n");
|
|
rsp->errno = -EFAULT;
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_link);
|
|
}
|
|
|
|
rsp->errno = qtfs_syscall_linkat(AT_FDCWD, userp->userp, AT_FDCWD, userp->userp2, 0);
|
|
qtfs_info("handle link new:%s old:%s return %d\n", newname, oldname, rsp->errno);
|
|
rsp->ret = rsp->errno == 0 ? QTFS_OK : QTFS_ERR;
|
|
return sizeof(struct qtrsp_link);
|
|
}
|
|
|
|
int handle_symlink(struct qtserver_arg *arg)
|
|
{
|
|
char *oldname, *newname;
|
|
struct qtreq_symlink *req = (struct qtreq_symlink *)REQ(arg);
|
|
struct qtrsp_symlink *rsp = (struct qtrsp_symlink *)RSP(arg);
|
|
int error;
|
|
struct dentry *dentry;
|
|
struct path path;
|
|
unsigned int lookup_flags = 0;
|
|
|
|
if (req->d.newlen >= sizeof(req->path) || req->d.newlen + req->d.oldlen > sizeof(req->path)) {
|
|
qtfs_err("newlen:%lu oldlen:%lu is too big", req->d.newlen, req->d.oldlen);
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_symlink);
|
|
}
|
|
newname = req->path;
|
|
oldname = &req->path[req->d.newlen];
|
|
retry:
|
|
dentry = kern_path_create(AT_FDCWD, newname, &path, lookup_flags);
|
|
error = QTFS_PTR_ERR(dentry);
|
|
if (IS_ERR_OR_NULL(dentry)) {
|
|
rsp->ret = QTFS_ERR;
|
|
qtfs_err("handle_symlink: newname(%s), oldname(%s) in kern_path_create %d\n", newname, oldname, error);
|
|
return sizeof(struct qtrsp_symlink);
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
rsp->errno = vfs_symlink(&nop_mnt_idmap, path.dentry->d_inode, dentry, oldname);
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
rsp->errno = vfs_symlink(&init_user_ns, path.dentry->d_inode, dentry, oldname);
|
|
#else
|
|
rsp->errno = vfs_symlink(path.dentry->d_inode, dentry, oldname);
|
|
#endif
|
|
done_path_create(&path, dentry);
|
|
if (rsp->errno == -ESTALE && !(lookup_flags & LOOKUP_REVAL)) {
|
|
lookup_flags |= LOOKUP_REVAL;
|
|
goto retry;
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle_symlink: newname(%s), oldname(%s) success\n", newname, oldname);
|
|
qtfs_inode_info_fill(&rsp->inode_info, dentry->d_inode);
|
|
return sizeof(struct qtrsp_symlink);
|
|
}
|
|
|
|
int handle_getlink(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_getlink *req = (struct qtreq_getlink *)REQ(arg);
|
|
struct qtrsp_getlink *rsp = (struct qtrsp_getlink *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
if (strlen(req->path) < 0 || strlen(req->path) + 1 > sizeof(req->path) || copy_to_user(userp->userp, req->path, strlen(req->path) + 1)) {
|
|
qtfs_err("handle getlink<%s> copy to userp failed.\n", req->path);
|
|
rsp->errno = -EFAULT;
|
|
goto err_handle;
|
|
}
|
|
rsp->errno = qtfs_syscall_readlinkat(AT_FDCWD, userp->userp, userp->userp2, userp->size);
|
|
if (rsp->errno < 0 || rsp->errno > MAX_PATH_LEN) {
|
|
qtfs_err("handle getlink<%s> do readlinkat failed, errno:%d\n", req->path, rsp->errno);
|
|
goto err_handle;
|
|
}
|
|
if (copy_from_user(rsp->path, userp->userp2, rsp->errno)) {
|
|
qtfs_err("handle getlink<%s> copy from user failed, len:%d.", req->path, rsp->errno);
|
|
rsp->errno = -EFAULT;
|
|
goto err_handle;
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle getlink<%s> ok, len:%d link:%s.", req->path, rsp->errno, rsp->path);
|
|
return sizeof(struct qtrsp_getlink) - sizeof(rsp->path) + strlen(rsp->path) + 1;
|
|
|
|
err_handle:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_getlink) - sizeof(rsp->path);
|
|
}
|
|
|
|
int handle_rename(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_rename *req = (struct qtreq_rename *)REQ(arg);
|
|
struct qtrsp_rename *rsp = (struct qtrsp_rename *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
if (!in_white_list(req->path, QTFS_WHITELIST_RENAME)) {
|
|
rsp->errno = -ENOENT;
|
|
goto err_handle;
|
|
}
|
|
if (strlen(req->path) + 1 > sizeof(req->path) || req->d.oldlen >= sizeof(req->path) ||
|
|
strlen(req->path) + strlen(&req->path[req->d.oldlen]) >= sizeof(req->path) ||
|
|
strlen(req->path) + 1 > userp->size || strlen(&req->path[req->d.oldlen]) + 1 > userp->size) {
|
|
qtfs_err("invalid req msg");
|
|
rsp->errno = -EFAULT;
|
|
goto err_handle;
|
|
}
|
|
if (copy_to_user(userp->userp, req->path, strlen(req->path) + 1) ||
|
|
copy_to_user(userp->userp2, &req->path[req->d.oldlen], strlen(&req->path[req->d.oldlen]) + 1)) {
|
|
qtfs_err("handle rename copy to userp failed.\n");
|
|
rsp->errno = -EFAULT;
|
|
goto err_handle;
|
|
}
|
|
rsp->errno = qtfs_syscall_renameat2(AT_FDCWD, userp->userp, AT_FDCWD, userp->userp2, 0);
|
|
|
|
err_handle:
|
|
rsp->ret = (rsp->errno < 0) ? QTFS_ERR : QTFS_OK;
|
|
qtfs_info("handle rename oldname:%s newname:%s ret:%d %s", req->path, &req->path[req->d.oldlen], rsp->errno,
|
|
(rsp->errno < 0) ? "failed" : "successed");
|
|
return sizeof(struct qtrsp_rename);
|
|
}
|
|
|
|
int handle_xattrlist(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_xattrlist *req = (struct qtreq_xattrlist *)REQ(arg);
|
|
struct qtrsp_xattrlist *rsp = (struct qtrsp_xattrlist *)RSP(arg);
|
|
struct path path;
|
|
int ret = 0;
|
|
ssize_t size = 0, buffer_size = 0;
|
|
int i = 0;
|
|
|
|
buffer_size = (req->buffer_size > sizeof(rsp->name)) ? sizeof(rsp->name) : req->buffer_size;
|
|
ret = kern_path(req->path, 0, &path);
|
|
if (ret) {
|
|
qtfs_err("handle xattr list path error.\n");
|
|
rsp->d.size = -ENOENT;
|
|
goto err_handle;
|
|
}
|
|
size = vfs_listxattr(path.dentry, buffer_size == 0 ? NULL : rsp->name, buffer_size);
|
|
path_put(&path);
|
|
if (size < 0) {
|
|
qtfs_err("handle list xattr failed, errno:%ld.\n", size);
|
|
rsp->d.size = size;
|
|
goto err_handle;
|
|
}
|
|
if (size == 0) {
|
|
rsp->d.size = size;
|
|
goto err_handle;
|
|
}
|
|
rsp->d.ret = QTFS_OK;
|
|
rsp->d.size = size;
|
|
while (i < size) {
|
|
qtfs_info("handle list xattr result:%s\n", &rsp->name[i]);
|
|
i += strlen(&rsp->name[i]) + 1;
|
|
}
|
|
return sizeof(struct qtrsp_xattrlist);
|
|
|
|
err_handle:
|
|
rsp->d.ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_xattrlist);
|
|
}
|
|
|
|
int handle_xattrset(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_xattrset *req = (struct qtreq_xattrset *)REQ(arg);
|
|
struct qtrsp_xattrset *rsp = (struct qtrsp_xattrset *)RSP(arg);
|
|
struct path path;
|
|
int ret = 0;
|
|
|
|
if (!in_white_list(req->buf, QTFS_WHITELIST_SETXATTR)) {
|
|
rsp->errno = -ENOENT;
|
|
goto err_handle;
|
|
}
|
|
|
|
ret = kern_path(req->buf, 0, &path);
|
|
if (ret) {
|
|
qtfs_err("handle xattrset path error, file:%s.\n", req->buf);
|
|
rsp->errno = -ENOENT;
|
|
goto err_handle;
|
|
}
|
|
if (req->d.pathlen + req->d.namelen + req->d.valuelen > sizeof(req->buf) - 3) {
|
|
qtfs_err("invalid len:%lu %lu %lu", req->d.pathlen, req->d.namelen, req->d.valuelen);
|
|
rsp->errno = -EFAULT;
|
|
path_put(&path);
|
|
goto err_handle;
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
rsp->errno = vfs_setxattr(&nop_mnt_idmap, path.dentry, &req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], req->d.valuelen, req->d.flags);
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
rsp->errno = vfs_setxattr(&init_user_ns, path.dentry, &req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], req->d.valuelen, req->d.flags);
|
|
#else
|
|
rsp->errno = vfs_setxattr(path.dentry, &req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], req->d.valuelen, req->d.flags);
|
|
#endif
|
|
qtfs_info("handle xattrset path:%s name:%s value:%s ret:%d size:%lu flags:%d", req->buf,
|
|
&req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], rsp->errno,
|
|
req->d.valuelen, req->d.flags);
|
|
path_put(&path);
|
|
return sizeof(struct qtrsp_xattrset);
|
|
|
|
err_handle:
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_xattrset);
|
|
}
|
|
|
|
int handle_xattrget(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_xattrget *req = (struct qtreq_xattrget *)REQ(arg);
|
|
struct qtrsp_xattrget *rsp = (struct qtrsp_xattrget *)RSP(arg);
|
|
struct path path;
|
|
int ret = 0;
|
|
ssize_t error = 0;
|
|
int len = 0;
|
|
char *kvalue = NULL;
|
|
|
|
ret = kern_path(req->path, 0, &path);
|
|
if (ret) {
|
|
qtfs_err("handle xattrget path error.\n");
|
|
rsp->d.errno = -ENOENT;
|
|
goto err_handle;
|
|
}
|
|
|
|
if (req->d.size > 0) {
|
|
if (req->d.size > XATTR_SIZE_MAX)
|
|
req->d.size = XATTR_SIZE_MAX;
|
|
|
|
if (req->d.pos > req->d.size) {
|
|
rsp->d.errno = -EINVAL;
|
|
path_put(&path);
|
|
goto err_handle;
|
|
}
|
|
kvalue = (char *)kvzalloc(req->d.size, GFP_KERNEL);
|
|
if (!kvalue) {
|
|
qtfs_err("handle xattrget kvzalloc failed, size:%d.\n", req->d.size);
|
|
rsp->d.errno = -ENOMEM;
|
|
path_put(&path);
|
|
goto err_handle;
|
|
}
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
error = vfs_getxattr(&nop_mnt_idmap, path.dentry, req->d.prefix_name, kvalue, req->d.size);
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
error = vfs_getxattr(&init_user_ns, path.dentry, req->d.prefix_name, kvalue, req->d.size);
|
|
#else
|
|
error = vfs_getxattr(path.dentry, req->d.prefix_name, kvalue, req->d.size);
|
|
#endif
|
|
path_put(&path);
|
|
if (error > 0) {
|
|
if (req->d.pos >= error) {
|
|
rsp->d.size = 0;
|
|
rsp->d.pos = req->d.pos;
|
|
goto end;
|
|
}
|
|
qtfs_info("handle getxattr: path:%s prefix name:%s : (%s - 0x%llx), size:%ld, reqpos:%d\n", req->path, req->d.prefix_name, kvalue, (__u64)kvalue, error, req->d.pos);
|
|
len = (error - req->d.pos) > sizeof(rsp->buf) ? sizeof(rsp->buf) : (error - req->d.pos);
|
|
rsp->d.size = len;
|
|
if (req->d.size > 0) {
|
|
memcpy(rsp->buf, &kvalue[req->d.pos], len);
|
|
}
|
|
rsp->d.pos = req->d.pos + len;
|
|
} else {
|
|
rsp->d.errno = error;
|
|
kvfree(kvalue);
|
|
goto err_handle;
|
|
}
|
|
end:
|
|
qtfs_info("handle getxattr successed file:%s result:%s", req->path, rsp->buf);
|
|
kvfree(kvalue);
|
|
rsp->d.ret = QTFS_OK;
|
|
return sizeof(struct qtrsp_xattrget) - sizeof(rsp->buf) + len;
|
|
|
|
err_handle:
|
|
rsp->d.ret = QTFS_ERR;
|
|
qtfs_err("handle getxattr failed, file:%s", req->path);
|
|
return sizeof(struct qtrsp_xattrget) - sizeof(rsp->buf);
|
|
}
|
|
|
|
int handle_syscall_mount(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_sysmount *req = (struct qtreq_sysmount *)REQ(arg);
|
|
struct qtrsp_sysmount *rsp = (struct qtrsp_sysmount *)RSP(arg);
|
|
char *dev_name, *dir_name, *type;
|
|
char *udir_name = NULL;
|
|
void *data_page;
|
|
char *udev_name = NULL;
|
|
char *utype = NULL;
|
|
char *udata = NULL;
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
if (req->d.dev_len + req->d.dir_len + req->d.type_len + req->d.data_len > sizeof(req->buf) - 4) {
|
|
qtfs_err("invalid msglen:%lu %lu %lu %lu", req->d.dev_len, req->d.dir_len, req->d.type_len, req->d.data_len);
|
|
rsp->errno = -EINVAL;
|
|
goto end;
|
|
}
|
|
dev_name = req->d.dev_len == 0 ? NULL : req->buf;
|
|
dir_name = &req->buf[req->d.dev_len];
|
|
type = req->d.type_len == 0 ? NULL : &req->buf[req->d.dev_len + req->d.dir_len];
|
|
if (req->d.data_len != 0)
|
|
data_page = &req->buf[req->d.dev_len + req->d.dir_len + req->d.type_len];
|
|
else
|
|
data_page = NULL;
|
|
|
|
if (strlen(dir_name) >= (userp->size / 2) ||
|
|
(dev_name != NULL && strlen(dev_name) >= (userp->size / 2)) ||
|
|
(type != NULL && strlen(type) >= (userp->size / 2)) ||
|
|
(data_page != NULL && strlen(data_page) >= (userp->size / 2))) {
|
|
qtfs_err("mount str len is too big dirname:%lu devname:%lu type:%lu data:%lu",
|
|
strlen(dir_name), (dev_name == NULL) ? 0 : strlen(dev_name),
|
|
(type == NULL) ? 0 : strlen(type),
|
|
(data_page == NULL) ? 0 : strlen(data_page));
|
|
rsp->errno = -EINVAL;
|
|
goto end;
|
|
}
|
|
udir_name = userp->userp;
|
|
udev_name = (dev_name == NULL) ? NULL : ((char *)userp->userp + userp->size / 2);
|
|
utype = (type == NULL) ? NULL : userp->userp2;
|
|
udata = (data_page == NULL) ? NULL : ((char *)userp->userp + userp->size / 2);
|
|
|
|
if (copy_to_user(udir_name, dir_name, strlen(dir_name) + 1) ||
|
|
(dev_name != NULL && copy_to_user(udev_name, dev_name, strlen(dev_name) + 1)) ||
|
|
(type != NULL && copy_to_user(utype, type, strlen(type) + 1)) ||
|
|
(data_page != NULL && copy_to_user(udata, data_page, strlen(data_page) + 1))) {
|
|
qtfs_err("syscall mount failed to copy to user");
|
|
rsp->errno = -ENOMEM;
|
|
goto end;
|
|
}
|
|
|
|
qtfs_info("handle syscall mount devname:%s dirname:%s type:%s data:%s\n", dev_name, dir_name, type,
|
|
(data_page == NULL) ? "nil" : (char *)data_page);
|
|
rsp->errno = qtfs_syscall_mount(udev_name, udir_name, utype, req->d.flags, udata);
|
|
if (rsp->errno < 0)
|
|
qtfs_err("handle syscall mount failed devname:%s dirname:%s type:%s data:%s, errno:%d\n",
|
|
dev_name, dir_name, type, (char *)data_page, rsp->errno);
|
|
|
|
end:
|
|
return sizeof(struct qtrsp_sysmount);
|
|
}
|
|
|
|
int handle_syscall_umount(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_sysumount *req = (struct qtreq_sysumount *)REQ(arg);
|
|
struct qtrsp_sysumount *rsp = (struct qtrsp_sysumount *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s *)USERP(arg);
|
|
|
|
qtfs_info("handle umount path:%s\n", req->buf);
|
|
if (strlen(req->buf) + 1 > userp->size || strlen(req->buf) >= sizeof(req->buf)) {
|
|
qtfs_err("invalid msg");
|
|
rsp->errno = -EINVAL;
|
|
return sizeof(struct qtrsp_sysumount);
|
|
}
|
|
if (copy_to_user(userp->userp, req->buf, strlen(req->buf) + 1)) {
|
|
rsp->errno = -ENOMEM;
|
|
return sizeof(struct qtrsp_sysumount);
|
|
}
|
|
rsp->errno = qtfs_syscall_umount(userp->userp, req->flags);
|
|
if (rsp->errno)
|
|
qtfs_err("umount(%s) failed, errno:%d\n", req->buf, rsp->errno);
|
|
//dont need to path_put here.
|
|
return sizeof(struct qtrsp_sysumount);
|
|
}
|
|
|
|
#ifdef KVER_4_19
|
|
static bool qtfs_pipe_empty(struct pipe_inode_info *pipe)
|
|
{
|
|
int nrbufs;
|
|
|
|
nrbufs = pipe->nrbufs;
|
|
return nrbufs <= 0;
|
|
}
|
|
|
|
static bool qtfs_pipe_full(struct pipe_inode_info *pipe)
|
|
{
|
|
int nrbufs;
|
|
|
|
nrbufs = pipe->nrbufs;
|
|
return nrbufs >= pipe->buffers;
|
|
}
|
|
#else
|
|
static bool qtfs_pipe_empty(struct pipe_inode_info *pipe)
|
|
{
|
|
unsigned int head, tail;
|
|
|
|
head = READ_ONCE(pipe->head);
|
|
tail = READ_ONCE(pipe->tail);
|
|
|
|
return pipe_empty(head, tail);
|
|
}
|
|
|
|
static bool qtfs_pipe_full(struct pipe_inode_info *pipe)
|
|
{
|
|
unsigned int head, tail;
|
|
|
|
head = READ_ONCE(pipe->head);
|
|
tail = READ_ONCE(pipe->tail);
|
|
|
|
return pipe_full(head, tail, pipe->max_usage);
|
|
}
|
|
#endif
|
|
|
|
int handle_fifopoll(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_poll *req = (struct qtreq_poll *)REQ(arg);
|
|
struct qtrsp_poll *rsp = (struct qtrsp_poll *)RSP(arg);
|
|
struct file *filp = NULL;
|
|
struct pipe_inode_info *pipe;
|
|
struct inode *inode;
|
|
__poll_t mask;
|
|
struct poll_wqueues table;
|
|
poll_table *pt;
|
|
|
|
filp = fget(req->fd);
|
|
if (!filp) {
|
|
rsp->mask = EPOLLERR;
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_poll);
|
|
}
|
|
inode = filp->f_inode;
|
|
if (!S_ISFIFO(inode->i_mode)) {
|
|
msleep(1);
|
|
poll_initwait(&table);
|
|
pt = &table.pt;
|
|
mask = vfs_poll(filp, pt);
|
|
poll_freewait(&table);
|
|
goto end;
|
|
}
|
|
pipe = filp->private_data;
|
|
if (pipe == NULL) {
|
|
qtfs_err("file :%s pipe data is NULL.", filp->f_path.dentry->d_iname);
|
|
rsp->ret = QTFS_ERR;
|
|
rsp->mask = EPOLLERR;
|
|
fput(filp);
|
|
return sizeof(struct qtrsp_poll);
|
|
}
|
|
mask = 0;
|
|
if (filp->f_mode & FMODE_READ) {
|
|
if (!qtfs_pipe_empty(pipe))
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
|
if (!pipe->writers && filp->f_version != pipe->w_counter)
|
|
mask |= EPOLLHUP;
|
|
}
|
|
|
|
if (filp->f_mode & FMODE_WRITE) {
|
|
if (!qtfs_pipe_full(pipe))
|
|
mask |= EPOLLOUT | EPOLLWRNORM;
|
|
if (!pipe->readers)
|
|
mask |= EPOLLERR;
|
|
}
|
|
end:
|
|
rsp->mask = mask;
|
|
rsp->ret = QTFS_OK;
|
|
|
|
qtfs_info("handle fifo poll f_mode:%o: %s get poll mask 0x%x\n",
|
|
filp->f_mode, filp->f_path.dentry->d_iname, rsp->mask);
|
|
fput(filp);
|
|
return sizeof(struct qtrsp_poll);
|
|
}
|
|
|
|
int handle_epollctl(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_epollctl *req = (struct qtreq_epollctl *)REQ(arg);
|
|
struct qtrsp_epollctl *rsp = (struct qtrsp_epollctl *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s*)USERP(arg);
|
|
int ret;
|
|
struct epoll_event evt;
|
|
|
|
evt.data = (__u64)req->event.data;
|
|
evt.events = req->event.events;
|
|
if (copy_to_user(userp->userp, &evt, sizeof(struct epoll_event))) {
|
|
qtfs_err("copy to user failed.");
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_epollctl);
|
|
}
|
|
ret = qtfs_syscall_epoll_ctl(qtfs_epoll.epfd, req->op, req->fd, userp->userp);
|
|
if (ret < 0) {
|
|
qtfs_err("handle do epoll ctl failed, ret:%d.", ret);
|
|
rsp->ret = QTFS_ERR;
|
|
return sizeof(struct qtrsp_epollctl);
|
|
}
|
|
qtinfo_cntinc((req->op == EPOLL_CTL_ADD) ? QTINF_EPOLL_ADDFDS : QTINF_EPOLL_DELFDS);
|
|
rsp->ret = QTFS_OK;
|
|
qtfs_info("handle do epoll ctl success, fd:%d op:%x poll_t:%x.",
|
|
req->fd, req->op, (unsigned)req->event.events);
|
|
|
|
return sizeof(struct qtrsp_epollctl);
|
|
}
|
|
|
|
int handle_llseek(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_llseek *req = (struct qtreq_llseek *)REQ(arg);
|
|
struct qtrsp_llseek *rsp = (struct qtrsp_llseek *)RSP(arg);
|
|
|
|
qtfs_info("llseek get req fd:%d, off:%lld whence:%d.", req->fd, req->off, req->whence);
|
|
rsp->off = qtfs_syscall_lseek(req->fd, req->off, req->whence);
|
|
if (rsp->off < 0) {
|
|
qtfs_err("llseek ksys lseek return :%ld failed, req fd:%d off:%lld whence:%d.",
|
|
rsp->off, req->fd, req->off, req->whence);
|
|
rsp->ret = QTFS_ERR;
|
|
goto end;
|
|
}
|
|
rsp->ret = QTFS_OK;
|
|
end:
|
|
return sizeof(struct qtrsp_llseek);
|
|
}
|
|
|
|
int handle_exit(struct qtserver_arg *arg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int handle_null(struct qtserver_arg *arg)
|
|
{
|
|
qtfs_err("unknown events.");
|
|
return 0;
|
|
}
|
|
|
|
int remotesc_kill(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_sc_kill *req = (struct qtreq_sc_kill *)REQ(arg);
|
|
struct qtrsp_sc_kill *rsp = (struct qtrsp_sc_kill *)RSP(arg);
|
|
char tskcomm[TASK_COMM_LEN] = {0};
|
|
struct task_struct *t = qtfs_kern_syms.find_get_task_by_vpid((pid_t)req->pid);
|
|
if (req->signum == 0)
|
|
goto result;
|
|
if (!t) {
|
|
qtfs_err("Failed to get task by pid:%d", req->pid);
|
|
rsp->ret = -EINVAL;
|
|
goto end;
|
|
}
|
|
get_task_comm(tskcomm, t);
|
|
if (!in_white_list_exact(tskcomm, QTFS_WHITELIST_KILL)) {
|
|
qtfs_err("Failed to kill pid:%d, comm:%s not in kill white list", req->pid, tskcomm);
|
|
rsp->ret = -EPERM;
|
|
goto end;
|
|
}
|
|
result:
|
|
rsp->ret = qtfs_syscall_kill(req->pid, req->signum);
|
|
qtfs_info("Recv remote kill request, pid:%d signum:%d ret:%ld", req->pid, req->signum, rsp->ret);
|
|
end:
|
|
return sizeof(struct qtrsp_sc_kill);
|
|
}
|
|
|
|
int remotesc_sched_getaffinity(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_sc_sched_affinity *req = (struct qtreq_sc_sched_affinity *)REQ(arg);
|
|
struct qtrsp_sc_sched_affinity *rsp = (struct qtrsp_sc_sched_affinity *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s*)USERP(arg);
|
|
|
|
if (req->len > AFFINITY_MAX_LEN) {
|
|
qtfs_err("invalid len:%lu", req->len);
|
|
rsp->ret = -EINVAL;
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
rsp->ret = qtfs_syscall_sched_getaffinity(req->pid, req->len, userp->userp);
|
|
if (rsp->ret < 0) {
|
|
qtfs_err("get affinity failed ret:%ld", rsp->ret);
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
if (copy_from_user(rsp->user_mask_ptr, userp->userp, req->len)) {
|
|
qtfs_err("copy affinity failed");
|
|
rsp->len = 0;
|
|
rsp->ret = -EFAULT;
|
|
goto end;
|
|
}
|
|
rsp->len = req->len;
|
|
qtfs_info("pid:%d get affinity successed", req->pid);
|
|
return sizeof(struct qtrsp_sc_sched_affinity) + rsp->len * sizeof(unsigned long);
|
|
|
|
end:
|
|
return sizeof(struct qtrsp_sc_sched_affinity);
|
|
}
|
|
|
|
int remotesc_sched_setaffinity(struct qtserver_arg *arg)
|
|
{
|
|
struct qtreq_sc_sched_affinity *req = (struct qtreq_sc_sched_affinity *)REQ(arg);
|
|
struct qtrsp_sc_sched_affinity *rsp = (struct qtrsp_sc_sched_affinity *)RSP(arg);
|
|
struct qtfs_server_userp_s *userp = (struct qtfs_server_userp_s*)USERP(arg);
|
|
|
|
if (req->len > AFFINITY_MAX_LEN || req->len < 0) {
|
|
qtfs_err("invalid len:%lu", req->len);
|
|
rsp->ret = -EINVAL;
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
if (copy_to_user(userp->userp, req->user_mask_ptr, req->len)) {
|
|
qtfs_err("copy to user failed len:%lu", req->len);
|
|
rsp->ret = -EFAULT;
|
|
rsp->len = 0;
|
|
goto end;
|
|
}
|
|
rsp->ret = qtfs_syscall_sched_setaffinity(req->pid, req->len, userp->userp);
|
|
if (rsp->ret < 0) {
|
|
qtfs_err("set affinity failed, ret:%ld pid:%d len:%lu", rsp->ret, req->pid, req->len);
|
|
goto end;
|
|
}
|
|
qtfs_info("set affinity successed mask:%lx%lx", req->user_mask_ptr[0], req->user_mask_ptr[1]);
|
|
end:
|
|
return sizeof(struct qtrsp_sc_sched_affinity);
|
|
}
|
|
|
|
static struct qtserver_ops qtfs_server_handles[] = {
|
|
{QTFS_REQ_NULL, req_check_none, handle_null, "null"},
|
|
{QTFS_REQ_MOUNT, req_check_mount, handle_mount, "mount"},
|
|
{QTFS_REQ_OPEN, req_check_open, handle_open, "open"},
|
|
{QTFS_REQ_CLOSE, req_check_close, handle_close, "close"},
|
|
{QTFS_REQ_READ, req_check_none, handle_null, "read"},
|
|
{QTFS_REQ_READITER, req_check_readiter, handle_readiter, "readiter"},
|
|
{QTFS_REQ_WRITE, req_check_write, handle_write, "write"},
|
|
{QTFS_REQ_LOOKUP, req_check_lookup, handle_lookup, "lookup"},
|
|
{QTFS_REQ_READDIR, req_check_readdir, handle_readdir, "readdir"},
|
|
{QTFS_REQ_MKDIR, req_check_mkdir, handle_mkdir, "mkdir"},
|
|
{QTFS_REQ_RMDIR, req_check_rmdir, handle_rmdir, "rmdir"},
|
|
{QTFS_REQ_GETATTR, req_check_getattr, handle_getattr, "getattr"},
|
|
{QTFS_REQ_SETATTR, req_check_setattr, handle_setattr, "setattr"},
|
|
{QTFS_REQ_ICREATE, req_check_icreate, handle_icreate, "icreate"},
|
|
{QTFS_REQ_MKNOD, req_check_mknod, handle_mknod, "mknod"},
|
|
{QTFS_REQ_UNLINK, req_check_unlink, handle_unlink, "unlink"},
|
|
{QTFS_REQ_SYMLINK, req_check_symlink, handle_symlink, "symlink"},
|
|
{QTFS_REQ_LINK, req_check_link, handle_link, "link"},
|
|
{QTFS_REQ_GETLINK, req_check_getlink, handle_getlink, "getlink"},
|
|
{QTFS_REQ_READLINK, req_check_none, handle_null, "readlink"},
|
|
{QTFS_REQ_RENAME, req_check_rename, handle_rename, "rename"},
|
|
|
|
{QTFS_REQ_XATTRLIST, req_check_xattrlist, handle_xattrlist, "xattrlist"},
|
|
{QTFS_REQ_XATTRGET, req_check_xattrget, handle_xattrget, "xattrget"},
|
|
{QTFS_REQ_XATTRSET, req_check_xattrset, handle_xattrset, "xattrset"},
|
|
|
|
{QTFS_REQ_SYSMOUNT, req_check_sysmount, handle_syscall_mount, "sysmount"},
|
|
{QTFS_REQ_SYSUMOUNT, req_check_sysumount, handle_syscall_umount, "sysumount"},
|
|
{QTFS_REQ_FIFOPOLL, req_check_fifopoll, handle_fifopoll, "fifo_poll"},
|
|
|
|
{QTFS_REQ_STATFS, req_check_statfs, handle_statfs, "statfs"},
|
|
{QTFS_REQ_IOCTL, req_check_ioctl, handle_ioctl, "ioctl"},
|
|
|
|
{QTFS_REQ_EPOLL_CTL, req_check_epoll_ctl, handle_epollctl, "epollctl"},
|
|
{QTFS_REQ_EPOLL_EVENT, req_check_none, NULL, "epollevent"},
|
|
|
|
{QTFS_REQ_LLSEEK, req_check_llseek, handle_llseek, "llseek"},
|
|
|
|
// remote syscall or capability
|
|
{QTFS_SC_KILL, req_check_sc_kill, remotesc_kill, "remotesc_kill"},
|
|
{QTFS_SC_SCHED_GETAFFINITY, req_check_sc_sched_getaffinity, remotesc_sched_getaffinity, "sched_getaffinity"},
|
|
{QTFS_SC_SCHED_SETAFFINITY, req_check_sc_sched_setaffinity, remotesc_sched_setaffinity, "sched_setaffinity"},
|
|
|
|
{QTFS_REQ_EXIT, req_check_none, handle_exit, "exit"}, // keep this handle at the end
|
|
};
|
|
|
|
int qtfs_conn_server_run(struct qtfs_conn_var_s *pvar)
|
|
{
|
|
int ret;
|
|
struct qtreq *req;
|
|
struct qtreq *rsp;
|
|
unsigned long totalproc = 0;
|
|
|
|
req = pvar->vec_recv.iov_base;
|
|
rsp = pvar->vec_send.iov_base;
|
|
do {
|
|
ret = qtfs_conn_recv_block(pvar);
|
|
if (ret == -EPIPE) {
|
|
qtfs_err("qtfs server thread recv EPIPE, restart the connection.");
|
|
qtfs_sm_reconnect(pvar);
|
|
break;
|
|
}
|
|
if (ret < 0)
|
|
break;
|
|
if (req->type >= QTFS_REQ_INV) {
|
|
qtfs_err("qtfs server recv unknown operate type:%d\n", req->type);
|
|
rsp->type = req->type;
|
|
rsp->len = 0;
|
|
rsp->err = QTFS_ERR;
|
|
} else {
|
|
struct qtserver_arg arg;
|
|
arg.data = req->data;
|
|
arg.out = rsp->data;
|
|
if (qtfs_server_handles[req->type].precheck((void *)req->data) == QTFS_CHECK_ERR) {
|
|
rsp->type = req->type;
|
|
rsp->len = 0;
|
|
rsp->err = QTFS_ERR;
|
|
qtinfo_reqcheckinc(req->type);
|
|
qtfs_err("qtfs server req type:%u precheck failed.", req->type);
|
|
goto out;
|
|
}
|
|
read_lock(&g_userp_rwlock);
|
|
arg.userp = &qtfs_userps[pvar->cur_threadidx];
|
|
if (arg.userp->userp == NULL || arg.userp->userp2 == NULL)
|
|
qtfs_err("server run userp or userp2 is invalid");
|
|
rsp->len = qtfs_server_handles[req->type].handle(&arg);
|
|
read_unlock(&g_userp_rwlock);
|
|
rsp->type = req->type;
|
|
rsp->err = QTFS_OK;
|
|
totalproc++;
|
|
qtinfo_recvinc(req->type);
|
|
}
|
|
if (rsp->len > QTFS_REQ_MAX_LEN) {
|
|
qtfs_crit("handle rsp len error type:%d len:%lu", rsp->type, rsp->len);
|
|
WARN_ON(1);
|
|
rsp->len = QTFS_REQ_MAX_LEN - 1;
|
|
rsp->err = QTFS_ERR;
|
|
}
|
|
out:
|
|
rsp->seq_num = req->seq_num;
|
|
pvar->vec_send.iov_len = QTFS_MSG_HEAD_LEN + rsp->len;
|
|
qtfs_debug("Server thread:%d count:%lu recv len:%d type:%d(%s) seq_num:%lu, reqlen:%lu, resp len:%lu, rsp threadidx:%d.\n",
|
|
pvar->cur_threadidx, totalproc, ret, req->type, (req->type >= QTFS_REQ_INV) ? "null" : qtfs_server_handles[req->type].str,
|
|
req->seq_num, req->len, pvar->vec_send.iov_len, pvar->cur_threadidx);
|
|
ret = qtfs_conn_send(pvar);
|
|
if (ret == -EPIPE) {
|
|
qtfs_err("qtfs server send get EPIPE, just restart the connection\n");
|
|
qtfs_sm_reconnect(pvar);
|
|
break;
|
|
}
|
|
if (ret < 0) {
|
|
qtfs_err("conn send failed, ret:%d\n", ret);
|
|
WARN_ON(1);
|
|
}
|
|
qtinfo_sendinc(rsp->type);
|
|
} while(0);
|
|
|
|
return (ret < 0) ? QTERROR : QTOK;
|
|
}
|
|
|