From 98fd7777a63ab5828a11eb64155fb89859c3210d Mon Sep 17 00:00:00 2001 From: yangxin <245051644@qq.com> Date: Fri, 10 Feb 2023 17:23:35 +0800 Subject: [PATCH 1/2] Add udsproxy, add whitelist to qtfs and rexec, fix errors. Signed-off-by: yangxin <245051644@qq.com> (cherry picked from commit 4141866c23832890a05efa9861d8c8ecf2047b6b) --- 0005-Add-whitelist-of-qtfs.patch | 896 ++++ ...-Fix-error-of-getxattr-and-listxattr.patch | 354 ++ 0007-Add-whitelist-of-rexec.patch | 197 + 0008-Add-udsproxy.patch | 2812 ++++++++++++ 0009-Add-rexec-shim.patch | 3954 +++++++++++++++++ dpu-utilities.spec | 20 +- v1.1.tar.gz | Bin 7 files changed, 8230 insertions(+), 3 deletions(-) create mode 100644 0005-Add-whitelist-of-qtfs.patch create mode 100644 0006-Fix-error-of-getxattr-and-listxattr.patch create mode 100644 0007-Add-whitelist-of-rexec.patch create mode 100644 0008-Add-udsproxy.patch create mode 100644 0009-Add-rexec-shim.patch mode change 100755 => 100644 v1.1.tar.gz diff --git a/0005-Add-whitelist-of-qtfs.patch b/0005-Add-whitelist-of-qtfs.patch new file mode 100644 index 0000000..26e1c68 --- /dev/null +++ b/0005-Add-whitelist-of-qtfs.patch @@ -0,0 +1,896 @@ +From 2052c2d81abe204e557b7b7d15be623caf26d7f7 Mon Sep 17 00:00:00 2001 +From: yangxin <245051644@qq.com> +Date: Fri, 10 Feb 2023 16:37:27 +0800 +Subject: [PATCH 1/5] Add whitelist of qtfs. + +Signed-off-by: yangxin <245051644@qq.com> +--- + qtfs/comm.h | 31 ++++ + qtfs/misc.c | 2 - + qtfs/qtfs/Makefile | 2 +- + qtfs/qtfs/qtfs-mod.h | 1 - + qtfs/qtfs/sb.c | 17 +- + qtfs/qtfs/syscall.c | 3 +- + qtfs/qtfs_server/Makefile | 4 +- + qtfs/qtfs_server/fsops.c | 145 ++++++++++++++---- + qtfs/qtfs_server/qtfs-server.c | 35 ++++- + qtfs/qtfs_server/qtfs-server.h | 1 + + qtfs/qtfs_server/user_engine.c | 51 +++++- + qtfs/req.h | 36 ++--- + .../whitelist/libvirt/qtfs_whitelist | 35 +++++ + 13 files changed, 286 insertions(+), 77 deletions(-) + create mode 100644 usecases/transparent-offload/whitelist/libvirt/qtfs_whitelist + +diff --git a/qtfs/comm.h b/qtfs/comm.h +index d639c19..901552c 100644 +--- a/qtfs/comm.h ++++ b/qtfs/comm.h +@@ -13,6 +13,7 @@ enum { + _QTFS_IOCTL_EXIT, + + _QTFS_IOCTL_ALLINFO, ++ _QTFS_IOCTL_WHITELIST, + _QTFS_IOCTL_CLEARALL, + + _QTFS_IOCTL_LOG_LEVEL, +@@ -26,6 +27,7 @@ enum { + #define QTFS_IOCTL_EPOLL_THREAD_RUN _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_EPOLL_THREAD_RUN) + #define QTFS_IOCTL_EXIT _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_EXIT) + #define QTFS_IOCTL_ALLINFO _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_ALLINFO) ++#define QTFS_IOCTL_WHITELIST _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_WHITELIST) + #define QTFS_IOCTL_CLEARALL _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_CLEARALL) + #define QTFS_IOCTL_LOGLEVEL _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_LOG_LEVEL) + #define QTFS_IOCTL_EPOLL_SUPPORT _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_EPOLL_SUPPORT) +@@ -42,6 +44,35 @@ struct qtfs_server_userp_s { + void *userp2; + }; + ++ ++enum { ++ QTFS_WHITELIST_OPEN, ++ QTFS_WHITELIST_WRITE, ++ QTFS_WHITELIST_READ, ++ QTFS_WHITELIST_READDIR, ++ QTFS_WHITELIST_MKDIR, ++ QTFS_WHITELIST_RMDIR, ++ QTFS_WHITELIST_CREATE, ++ QTFS_WHITELIST_UNLINK, ++ QTFS_WHITELIST_RENAME, ++ QTFS_WHITELIST_SETATTR, ++ QTFS_WHITELIST_SETXATTR, ++ QTFS_WHITELIST_MOUNT, ++ QTFS_WHITELIST_MAX, ++}; ++ ++ ++struct wl_item { ++ int len; ++ char path[4096]; ++}; ++ ++struct whitelist { ++ int len; ++ int type; ++ struct wl_item wl[0]; ++}; ++ + struct qtfs_thread_init_s { + int thread_nums; + struct qtfs_server_userp_s *userp; +diff --git a/qtfs/misc.c b/qtfs/misc.c +index 90c8d36..98222bd 100644 +--- a/qtfs/misc.c ++++ b/qtfs/misc.c +@@ -61,7 +61,6 @@ void qtfs_req_size(void) + qtfs_diag_info->req_size[QTFS_REQ_MOUNT] = sizeof(struct qtreq_mount); + qtfs_diag_info->req_size[QTFS_REQ_OPEN] = sizeof(struct qtreq_open); + qtfs_diag_info->req_size[QTFS_REQ_CLOSE] = sizeof(struct qtreq_close); +- qtfs_diag_info->req_size[QTFS_REQ_READ] = sizeof(struct qtreq_read); + qtfs_diag_info->req_size[QTFS_REQ_READITER] = sizeof(struct qtreq_readiter); + qtfs_diag_info->req_size[QTFS_REQ_WRITE] = sizeof(struct qtreq_write); + qtfs_diag_info->req_size[QTFS_REQ_LOOKUP] = sizeof(struct qtreq_lookup); +@@ -92,7 +91,6 @@ void qtfs_req_size(void) + qtfs_diag_info->rsp_size[QTFS_REQ_MOUNT] = sizeof(struct qtrsp_mount); + qtfs_diag_info->rsp_size[QTFS_REQ_OPEN] = sizeof(struct qtrsp_open); + qtfs_diag_info->rsp_size[QTFS_REQ_CLOSE] = sizeof(struct qtrsp_close); +- qtfs_diag_info->rsp_size[QTFS_REQ_READ] = sizeof(struct qtrsp_read); + qtfs_diag_info->rsp_size[QTFS_REQ_READITER] = sizeof(struct qtrsp_readiter); + qtfs_diag_info->rsp_size[QTFS_REQ_WRITE] = sizeof(struct qtrsp_write); + qtfs_diag_info->rsp_size[QTFS_REQ_LOOKUP] = sizeof(struct qtrsp_lookup); +diff --git a/qtfs/qtfs/Makefile b/qtfs/qtfs/Makefile +index f3c6014..f03ec52 100644 +--- a/qtfs/qtfs/Makefile ++++ b/qtfs/qtfs/Makefile +@@ -11,4 +11,4 @@ qtfs: + + clean: + make -C $(KBUILD) M=$(PWD) clean +- rm -rf ../*.o ++ rm -rf ../*.o ../.*.o.cmd +diff --git a/qtfs/qtfs/qtfs-mod.h b/qtfs/qtfs/qtfs-mod.h +index 5a30868..6ba7a4d 100644 +--- a/qtfs/qtfs/qtfs-mod.h ++++ b/qtfs/qtfs/qtfs-mod.h +@@ -35,7 +35,6 @@ extern struct kmem_cache *qtfs_inode_priv_cache; + + struct private_data { + int fd; +- unsigned long long file; + }; + + struct qtfs_inode_priv { +diff --git a/qtfs/qtfs/sb.c b/qtfs/qtfs/sb.c +index 06ce402..9374cfb 100644 +--- a/qtfs/qtfs/sb.c ++++ b/qtfs/qtfs/sb.c +@@ -196,7 +196,6 @@ int qtfs_open(struct inode *inode, struct file *file) + 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->file = rsp->file; + data->fd = rsp->fd; + WARN_ON(file->private_data); + file->private_data = data; +@@ -287,9 +286,9 @@ ssize_t qtfs_readiter(struct kiocb *kio, struct iov_iter *iov) + return -ENOMEM; + } + +- req->file = private->file; +- if (req->file <= 0) { +- qtfs_err("qtfs_readiter: invalid file(0x%llx)", req->file); ++ req->fd = private->fd; ++ if (req->fd <= 0) { ++ qtfs_err("qtfs_readiter: invalid file(0x%llx)", req->fd); + qtfs_conn_put_param(pvar); + return -EINVAL; + } +@@ -359,9 +358,9 @@ ssize_t qtfs_writeiter(struct kiocb *kio, struct iov_iter *iov) + return -ENOMEM; + } + +- req->d.file = private->file; +- if (req->d.file < 0) { +- qtfs_err("qtfs_write: invalid file(0x%llx)", req->d.file); ++ req->d.fd = private->fd; ++ if (req->d.fd < 0) { ++ qtfs_err("qtfs_write: invalid file(0x%llx)", req->d.fd); + qtfs_conn_put_param(pvar); + return -EINVAL; + } +@@ -617,7 +616,7 @@ qtfsfifo_poll(struct file *filp, poll_table *wait) + + p = &priv->readq.head; + +- if (IS_ERR((void *)fpriv->file) || (void *)fpriv->file == NULL) { ++ if (fpriv->fd < 0) { + qtfs_err("fifo poll priv file invalid."); + return 0; + } +@@ -627,7 +626,7 @@ qtfsfifo_poll(struct file *filp, poll_table *wait) + return 0; + } + req = qtfs_sock_msg_buf(pvar, QTFS_SEND); +- req->file = fpriv->file; ++ req->fd = fpriv->fd; + rsp = qtfs_remote_run(pvar, QTFS_REQ_FIFOPOLL, sizeof(struct qtreq_poll)); + if (IS_ERR(rsp) || rsp == NULL) { + qtfs_conn_put_param(pvar); +diff --git a/qtfs/qtfs/syscall.c b/qtfs/qtfs/syscall.c +index 85cfbbe..2912f48 100644 +--- a/qtfs/qtfs/syscall.c ++++ b/qtfs/qtfs/syscall.c +@@ -110,7 +110,6 @@ static void do_epoll_ctl_remote(int op, struct epoll_event __user *event, struct + } + req = qtfs_sock_msg_buf(pvar, QTFS_SEND); + req->fd = priv->fd; +- req->file = priv->file; + req->op = op; + if (ep_op_has_event(op) && copy_from_user(&tmp, event, sizeof(struct epoll_event))) { + qtfs_err("qtfs do epoll ctl remote copy from user failed."); +@@ -131,7 +130,7 @@ static void do_epoll_ctl_remote(int op, struct epoll_event __user *event, struct + } else { + qtinfo_cntinc(QTINF_EPOLL_DELFDS); + } +- qtfs_info("qtfs do epoll ctl remote success, fd:%d file:%lx.", req->fd, (unsigned long)req->file); ++ qtfs_info("qtfs do epoll ctl remote success, fd:%d.", req->fd); + qtfs_conn_put_param(pvar); + return; + } +diff --git a/qtfs/qtfs_server/Makefile b/qtfs/qtfs_server/Makefile +index c1c5ef6..9c6bcd5 100644 +--- a/qtfs/qtfs_server/Makefile ++++ b/qtfs/qtfs_server/Makefile +@@ -10,9 +10,9 @@ qtfs_server: + make -C $(KBUILD) M=$(PWD) modules + + engine: +- gcc -O2 -o engine user_engine.c -lpthread -I../ -DQTFS_SERVER ++ gcc -O2 -o engine user_engine.c -lpthread -lglib-2.0 -I../ -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -DQTFS_SERVER + + clean: + make -C $(KBUILD) M=$(PWD) clean + rm -rf engine +- rm -rf ../*.o ++ rm -rf ../*.o ../.*.o.cmd +diff --git a/qtfs/qtfs_server/fsops.c b/qtfs/qtfs_server/fsops.c +index 48ec7ab..d00db6d 100644 +--- a/qtfs/qtfs_server/fsops.c ++++ b/qtfs/qtfs_server/fsops.c +@@ -23,6 +23,21 @@ + #define RSP(arg) (arg->out) + #define USERP(arg) (arg->userp) + ++bool in_white_list(char *path, int type) ++{ ++ if (!whitelist[type]) { ++ return true; ++ } ++ int i, in_wl = -1; ++ for (i = 0; i < whitelist[type]->len; i++) { ++ if (!strncmp(path, whitelist[type]->wl[i].path, whitelist[type]->wl[i].len)){ ++ in_wl = i; ++ break; ++ } ++ } ++ return in_wl != -1; ++} ++ + static inline void qtfs_inode_info_fill(struct inode_info *ii, struct inode *inode) + { + ii->mode = inode->i_mode; +@@ -55,7 +70,6 @@ static int handle_ioctl(struct qtserver_arg *arg) + 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); +- + file = filp_open(req->path, O_RDONLY, 0); + if (err_ptr(file)) { + qtfs_err("handle ioctl error, path:<%s> failed.\n", req->path); +@@ -188,9 +202,13 @@ static int handle_statfs(struct qtserver_arg *arg) + static int handle_mount(struct qtserver_arg *arg) + { + struct path path; +- int ret; ++ int ret, i, in_wl = -1; + 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; ++ return sizeof(rsp->ret); ++ } + + ret = kern_path(req->path, LOOKUP_DIRECTORY, &path); + if (ret) { +@@ -208,11 +226,15 @@ int handle_open(struct qtserver_arg *arg) + { + int fd; + int ret; +- struct fd f; +- struct file *file = NULL; + 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_err("handle open path:%s not permited", req->path); ++ rsp->ret = QTFS_ERR; ++ rsp->fd = -EACCES; ++ return sizeof(struct qtrsp_open); ++ } + + ret = copy_to_user(userp->userp, req->path, strlen(req->path)+1); + if (ret) { +@@ -235,26 +257,11 @@ int handle_open(struct qtserver_arg *arg) + } + rsp->ret = QTFS_ERR; + rsp->fd = fd; +- rsp->file = 0; + return sizeof(struct qtrsp_open); + } + +- f = fdget(fd); +- file = f.file; +- if (err_ptr(file)) { +- rsp->ret = QTFS_ERR; +- rsp->fd = PTR_ERR(file); +- // must close_fd(fd)? +- WARN_ON(1); +- qtfs_err("handle open get file pointer of <<%s>> error, fd:%d file err:%d.", req->path, fd, rsp->fd); +- // XXX: fileclose here? +- } else { +- rsp->ret = QTFS_OK; +- rsp->file = (__u64)file; +- rsp->fd = fd; +- } +- qtfs_info("handle open file :%s fd:%d filep:%lx.", req->path, fd, (unsigned long)rsp->file); +- fdput(f); ++ rsp->ret = QTFS_OK; ++ rsp->fd = fd; + return sizeof(struct qtrsp_open); + } + +@@ -279,18 +286,30 @@ int handle_close(struct qtserver_arg *arg) + static int handle_readiter(struct qtserver_arg *arg) + { + struct file *file = NULL; ++ char *pathbuf, *fullname; + 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); + size_t maxlen = (req->len >= sizeof(rsp->readbuf)) ? (sizeof(rsp->readbuf) - 1) : req->len; + +- file = (struct file *)req->file; ++ file = fget(req->fd); ++ pathbuf = __getname(); ++ fullname = file_path(file, pathbuf, PATH_MAX); ++ if (!in_white_list(fullname, QTFS_WHITELIST_READ)) { ++ qtfs_err("%s not in whitelist.\n", fullname); ++ __putname(pathbuf); ++ rsp->d.ret = QTFS_ERR; ++ rsp->d.len = 0; ++ rsp->d.errno = -ENOENT; ++ goto end; ++ } ++ __putname(pathbuf); + if (err_ptr(file)) { + qtfs_err("handle readiter error, open failed, file:%p.\n", file); + rsp->d.ret = QTFS_ERR; + rsp->d.len = 0; + rsp->d.errno = -ENOENT; +- return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf) + rsp->d.len; ++ goto end; + } + if (file->f_op->read) { + int idx = 0; +@@ -326,23 +345,35 @@ static int handle_readiter(struct qtserver_arg *arg) + + qtfs_info("handle readiter file:<%s>, len:%lu, rsplen:%ld, pos:%lld, ret:%d errno:%d.\n", + file->f_path.dentry->d_iname, req->len, rsp->d.len, req->pos, rsp->d.ret, rsp->d.errno); ++end: ++ fput(file); + return sizeof(struct qtrsp_readiter) - sizeof(rsp->readbuf) + rsp->d.len; + } + + static int handle_write(struct qtserver_arg *arg) + { + struct file *file = NULL; ++ char *pathbuf, *fullname; + 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; + +- file = (struct file *)req->d.file; ++ file = fget(req->d.fd); ++ pathbuf = __getname(); ++ fullname = file_path(file, pathbuf, PATH_MAX); ++ if (!in_white_list(fullname, QTFS_WHITELIST_WRITE)) { ++ kfree(pathbuf); ++ rsp->ret = QTFS_ERR; ++ rsp->len = 0; ++ goto end; ++ } ++ __putname(pathbuf); + if (err_ptr(file)) { + qtfs_err("qtfs handle write error, filp:<%p> open failed.\n", file); + rsp->ret = QTFS_ERR; + rsp->len = 0; +- return sizeof(struct qtrsp_write); ++ goto end; + } + + file->f_mode = req->d.mode; +@@ -372,6 +403,8 @@ static int handle_write(struct qtserver_arg *arg) + rsp->ret = (rsp->len <= 0) ? QTFS_ERR : QTFS_OK; + qtfs_info("handle write file<%s> %s, write len:%ld pos:%lld mode:%o flags:%x.", file->f_path.dentry->d_iname, + (rsp->ret == QTFS_ERR) ? "failed" : "succeded", rsp->len, req->d.pos, file->f_mode, file->f_flags); ++end: ++ fput(file); + return sizeof(struct qtrsp_write); + } + +@@ -438,6 +471,12 @@ static int handle_readdir(struct qtserver_arg *arg) + .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 (err_ptr(file)) { + qtfs_err("handle readdir error, filp:<%s> open failed.\n", req->path); +@@ -466,7 +505,11 @@ static int handle_mkdir(struct qtserver_arg *arg) + struct inode *inode; + struct path path; + int ret; +- ++ ++ if (!in_white_list(req->path, QTFS_WHITELIST_MKDIR)) { ++ rsp->errno = -EFAULT; ++ goto err; ++ } + if (copy_to_user(userp->userp, req->path, strlen(req->path) + 1)) { + qtfs_err("handle mkdir copy to userp failed.\n"); + rsp->errno = -EFAULT; +@@ -499,7 +542,11 @@ 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); +- ++ ++ if (!in_white_list(req->path, QTFS_WHITELIST_RMDIR)) { ++ rsp->errno = -EFAULT; ++ goto err; ++ } + if (copy_to_user(userp->userp, req->path, strlen(req->path) + 1)) { + qtfs_err("handle rmdir copy to userp failed.\n"); + rsp->errno = -EFAULT; +@@ -558,6 +605,12 @@ static int handle_setattr(struct qtserver_arg *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) { +@@ -610,6 +663,12 @@ int handle_icreate(struct qtserver_arg *arg) + 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 (err_ptr(file)) { +@@ -635,6 +694,12 @@ static int handle_mknod(struct qtserver_arg *arg) + 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); +@@ -668,6 +733,11 @@ 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); ++ ++ if (!in_white_list(req->path, QTFS_WHITELIST_UNLINK)) { ++ rsp->errno = -ENOENT; ++ return sizeof(struct qtrsp_unlink); ++ } + + rsp->errno = qtfs_kern_syms.do_unlinkat(AT_FDCWD, qtfs_kern_syms.getname_kernel(req->path)); + if (rsp->errno < 0) { +@@ -770,7 +840,11 @@ 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 (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"); +@@ -831,6 +905,12 @@ int handle_xattrset(struct qtserver_arg *arg) + struct path path; + int ret = 0; + ++ if (!in_white_list(req->buf, QTFS_WHITELIST_SETXATTR)) { ++ rsp->errno = -ENOENT; ++ rsp->ret = QTFS_ERR; ++ goto err_handle; ++ } ++ + ret = kern_path(req->buf, 0, &path); + if (ret) { + qtfs_err("handle xattrset path error, file:%s.\n", req->buf); +@@ -997,7 +1077,7 @@ int handle_fifopoll(struct qtserver_arg *arg) + struct poll_wqueues table; + poll_table *pt; + +- filp = (struct file *)req->file; ++ filp = fget(req->fd); + inode = filp->f_inode; + if (!S_ISFIFO(inode->i_mode)) { + msleep(1); +@@ -1011,6 +1091,7 @@ int handle_fifopoll(struct qtserver_arg *arg) + if (pipe == NULL) { + qtfs_err("file :%s pipe data is NULL.", filp->f_path.dentry->d_iname); + rsp->ret = QTFS_ERR; ++ fput(filp); + return sizeof(struct qtrsp_poll); + } + head = READ_ONCE(pipe->head); +@@ -1035,6 +1116,7 @@ end: + + qtfs_info("handle fifo poll f_mode:%o: %s get poll mask 0x%x poll:%lx\n", + filp->f_mode, filp->f_path.dentry->d_iname, rsp->mask, (unsigned long)filp->f_op->poll); ++ fput(filp); + return sizeof(struct qtrsp_poll); + } + +@@ -1055,8 +1137,8 @@ int handle_epollctl(struct qtserver_arg *arg) + } + 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 file:%lx op:%x data:%lx poll_t:%x.", +- req->fd, (unsigned long)req->file, req->op, req->event.data, (unsigned)req->event.events); ++ qtfs_info("handle do epoll ctl success, fd:%d op:%x data:%lx poll_t:%x.", ++ req->fd, req->op, req->event.data, (unsigned)req->event.events); + + return sizeof(struct qtrsp_epollctl); + } +@@ -1197,3 +1279,4 @@ int qtfs_sock_server_run(struct qtfs_sock_var_s *pvar) + qtfs_sock_msg_clear(pvar); + return (ret < 0) ? QTERROR : QTOK; + } ++ +diff --git a/qtfs/qtfs_server/qtfs-server.c b/qtfs/qtfs_server/qtfs-server.c +index bcd60b7..b0b8ab0 100644 +--- a/qtfs/qtfs_server/qtfs-server.c ++++ b/qtfs/qtfs_server/qtfs-server.c +@@ -28,6 +28,8 @@ struct qtfs_server_epoll_s qtfs_epoll = { + .events = NULL, + }; + ++struct whitelist* whitelist[QTFS_WHITELIST_MAX]; ++ + long qtfs_server_epoll_thread(struct qtfs_sock_var_s *pvar) + { + int n; +@@ -140,9 +142,10 @@ long qtfs_server_epoll_init(void) + + long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { +- int i; ++ int i, len; + long ret = 0; + struct qtfs_sock_var_s *pvar; ++ struct whitelist *tmp; + struct qtfs_thread_init_s init_userp; + switch (cmd) { + case QTFS_IOCTL_THREAD_INIT: +@@ -216,6 +219,26 @@ long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long a + case QTFS_IOCTL_LOGLEVEL: + ret = qtfs_misc_ioctl(file, cmd, arg); + break; ++ case QTFS_IOCTL_WHITELIST: ++ if (copy_from_user(&len, (void __user *)arg, sizeof(int))) { ++ qtfs_err("qtfs ioctl white init copy from user failed."); ++ return QTERROR; ++ } ++ tmp = (struct whitelist *)kmalloc(sizeof(struct whitelist) + sizeof(struct wl_item) * len, GFP_KERNEL); ++ ++ if (copy_from_user(tmp, (void __user *)arg, sizeof(struct whitelist) + sizeof(struct wl_item) * len)) { ++ qtfs_err("qtfs ioctl white init copy from user failed."); ++ return QTERROR; ++ } ++ ++ if (whitelist[tmp->type] != NULL) { ++ kfree(whitelist[tmp->type]); ++ } ++ whitelist[tmp->type] = tmp; ++ for (i = 0; i < whitelist[tmp->type]->len; i++) { ++ qtfs_err("init %d list:%d %s", tmp->type, i, whitelist[tmp->type]->wl[i].path); ++ } ++ break; + default: + qtfs_err("qtfs misc ioctl unknown cmd:%u.", cmd); + break; +@@ -226,7 +249,11 @@ long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long a + + static int __init qtfs_server_init(void) + { ++ int i; + qtfs_log_init(qtfs_log_level); ++ for (i = 0; i < QTFS_WHITELIST_MAX; i++) { ++ whitelist[i] = NULL; ++ } + qtfs_diag_info = (struct qtinfo *)kmalloc(sizeof(struct qtinfo), GFP_KERNEL); + if (qtfs_diag_info == NULL) + qtfs_err("kmalloc qtfs diag info failed."); +@@ -246,6 +273,7 @@ static int __init qtfs_server_init(void) + + static void __exit qtfs_server_exit(void) + { ++ int i; + qtfs_mod_exiting = true; + qtfs_server_thread_run = 0; + +@@ -269,6 +297,11 @@ static void __exit qtfs_server_exit(void) + kfree(qtfs_userps); + qtfs_userps = NULL; + } ++ for (i = 0; i < QTFS_WHITELIST_MAX; i++) { ++ if (whitelist[i] != NULL) { ++ kfree(whitelist[i]); ++ } ++ } + qtfs_misc_destroy(); + qtfs_info("qtfs server exit done.\n"); + return; +diff --git a/qtfs/qtfs_server/qtfs-server.h b/qtfs/qtfs_server/qtfs-server.h +index 8bcadf6..d10742a 100644 +--- a/qtfs/qtfs_server/qtfs-server.h ++++ b/qtfs/qtfs_server/qtfs-server.h +@@ -4,6 +4,7 @@ + extern int qtfs_server_thread_run; + extern struct qtfs_server_epoll_s qtfs_epoll; + extern int qtfs_mod_exiting; ++extern struct whitelist* whitelist[QTFS_WHITELIST_MAX]; + + struct qtserver_arg { + char *data; +diff --git a/qtfs/qtfs_server/user_engine.c b/qtfs/qtfs_server/user_engine.c +index a062b63..547935c 100644 +--- a/qtfs/qtfs_server/user_engine.c ++++ b/qtfs/qtfs_server/user_engine.c +@@ -9,11 +9,14 @@ + #include + #include + #include ++#include + + #include + + #include "comm.h" + ++char wl_type_str[QTFS_WHITELIST_MAX][10] = {"Open", "Write", "Read", "Readdir", "Mkdir", "Rmdir", "Create", "Unlink", "Rename", "Setattr", "Setxattr", "Mount"}; ++ + #define engine_out(info, ...) \ + do {\ + printf("[Engine::%s:%3d]"info"\n", __func__, __LINE__, ##__VA_ARGS__);\ +@@ -29,6 +32,10 @@ + printf("[ERROR:Engine::%s:%3d]"info"\n", __func__, __LINE__, ##__VA_ARGS__);\ + } while (0); + ++#define WHITELIST_FILE "/etc/qtfs/whitelist" ++ ++struct whitelist *whitelist[QTFS_WHITELIST_MAX]; ++ + struct engine_arg { + int psize; + int fd; +@@ -171,6 +178,46 @@ int qtfs_epoll_init(int fd) + return epfd; + } + ++static int qtfs_whitelist_transfer(int fd, GKeyFile *config, int type) ++{ ++ int64_t i, len; ++ char **items = g_key_file_get_string_list(config,wl_type_str[type],"Path",&len,NULL); ++ if (len == 0) { ++ engine_out("Can't find whitelist item %s", wl_type_str[type]); ++ return 0; ++ } ++ whitelist[type] = (struct whitelist *)malloc(sizeof(struct whitelist) + sizeof(struct wl_item) * len); ++ g_print("%s:\n", wl_type_str[type]); ++ whitelist[type]->len = len; ++ whitelist[type]->type = type; ++ for(i = 0; i < len;i++){ ++ printf("%s\n", items[i]); ++ whitelist[type]->wl[i].len = strlen(items[i]); ++ strcpy(whitelist[type]->wl[i].path, items[i]); ++ } ++ int ret = ioctl(fd, QTFS_IOCTL_WHITELIST, whitelist[type]); ++ free(items); ++ return ret; ++} ++ ++int qtfs_whitelist_init(int fd) ++{ ++ int ret, i; ++ GKeyFile *config = g_key_file_new(); ++ g_key_file_load_from_file(config, WHITELIST_FILE, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL); ++ for (i = 0; i < QTFS_WHITELIST_MAX; i++) { ++ ret = qtfs_whitelist_transfer(fd, config, i); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ g_key_file_free(config); ++ for (i = 0; i < QTFS_WHITELIST_MAX; i++) { ++ free(whitelist[i]); ++ } ++ return 0; ++} ++ + int main(int argc, char *argv[]) + { + if (argc != 3) { +@@ -192,6 +239,9 @@ int main(int argc, char *argv[]) + close(fd); + return -1; + } ++ if (qtfs_whitelist_init(fd)) { ++ goto end; ++ } + + umask(0); + +@@ -212,7 +262,6 @@ int main(int argc, char *argv[]) + engine_out("qtfs engine userp init failed."); + goto end; + } +- + struct engine_arg arg[QTFS_MAX_THREADS]; + for (int i = 0; i < thread_nums; i++) { + arg[i].psize = psize; +diff --git a/qtfs/req.h b/qtfs/req.h +index 0208667..3bcfa77 100644 +--- a/qtfs/req.h ++++ b/qtfs/req.h +@@ -12,29 +12,29 @@ enum qtreq_type { + QTFS_REQ_OPEN, + QTFS_REQ_CLOSE, + QTFS_REQ_READ, +- QTFS_REQ_READITER, //5 ++ QTFS_REQ_READITER, // 5 + QTFS_REQ_WRITE, + QTFS_REQ_LOOKUP, + QTFS_REQ_READDIR, + QTFS_REQ_MKDIR, +- QTFS_REQ_RMDIR, //10 ++ QTFS_REQ_RMDIR, // 10 + QTFS_REQ_GETATTR, + QTFS_REQ_SETATTR, + QTFS_REQ_ICREATE, + QTFS_REQ_MKNOD, +- QTFS_REQ_UNLINK, //15 ++ QTFS_REQ_UNLINK, // 15 + QTFS_REQ_SYMLINK, + QTFS_REQ_LINK, + QTFS_REQ_GETLINK, + QTFS_REQ_READLINK, +- QTFS_REQ_RENAME, //20 ++ QTFS_REQ_RENAME, // 20 + + QTFS_REQ_XATTRLIST, + QTFS_REQ_XATTRGET, + QTFS_REQ_XATTRSET, + + QTFS_REQ_SYSMOUNT, +- QTFS_REQ_SYSUMOUNT, //25 ++ QTFS_REQ_SYSUMOUNT, // 25 + QTFS_REQ_FIFOPOLL, + + QTFS_REQ_STATFS, +@@ -117,11 +117,11 @@ static inline void qtfs_nbytes_print(unsigned char *buf, int bytes) + #define QTFS_SEND_SIZE(stru, tailstr) sizeof(stru) - sizeof(tailstr) + strlen(tailstr) + 1 + + struct qtreq { +- unsigned int type; // operation type ++ unsigned int type; // operation type + unsigned int err; + unsigned long seq_num; // check code + size_t len; +- char data[QTFS_REQ_MAX_LEN]; // operation's private data ++ char data[QTFS_REQ_MAX_LEN]; // operation's private data + }; + + #define QTFS_MSG_LEN sizeof(struct qtreq) +@@ -169,7 +169,6 @@ struct qtreq_open { + }; + + struct qtrsp_open { +- __u64 file; + int fd; + int ret; + }; +@@ -182,25 +181,10 @@ struct qtrsp_close { + int ret; + }; + +-struct qtreq_read { +- size_t len; +- long long pos; +- __u64 file; +-}; +- +-struct qtrsp_read { +- struct qtrsp_read_len { +- int ret; +- ssize_t len; +- int errno; +- } d; +- char readbuf[QTFS_TAIL_LEN(struct qtrsp_read_len)]; +-}; +- + struct qtreq_readiter { + size_t len; + long long pos; +- __u64 file; ++ int fd; + }; + + struct qtrsp_readiter { +@@ -216,7 +200,7 @@ struct qtreq_write { + struct qtreq_write_len { + int buflen; + long long pos; +- __u64 file; ++ int fd; + long long flags; + long long mode; + } d; +@@ -505,7 +489,6 @@ struct qtrsp_sysumount { + + struct qtreq_poll { + int fd; +- __u64 file; + int qproc; + }; + +@@ -516,7 +499,6 @@ struct qtrsp_poll { + + + struct qtreq_epollctl { +- __u64 file; + int fd; + int op; + struct qtreq_epoll_event event; +diff --git a/usecases/transparent-offload/whitelist/libvirt/qtfs_whitelist b/usecases/transparent-offload/whitelist/libvirt/qtfs_whitelist +new file mode 100644 +index 0000000..d6e14ae +--- /dev/null ++++ b/usecases/transparent-offload/whitelist/libvirt/qtfs_whitelist +@@ -0,0 +1,35 @@ ++[Open] ++Path=/proc/sys/kernel/sched_autogroup_enabled;/proc/sys/vm;/sys/bus/pci;/sys/devices/pic;/sys/devices/system/node;/sys/kernel/mm;/sys/fs/cgroup;/home/VMs;/sys/fs/cgroup;/var/lib/libvirt/qemu;/sys/devices/system/cpu/online;/sys/module/kvm;/proc;/sys ++ ++[Write] ++Path=/proc/sys/kernel/sched_autogroup_enabled;/proc/sys/vm;/sys/bus/pci;/sys/devices/pic;/sys/devices/system/node;/sys/kernel/mm;/sys/fs/cgroup;/home/VMs;/sys/fs/cgroup;/var/lib/libvirt/qemu ++ ++[Readiter] ++Path=/sys/module/kvm;/proc;/home/VMs;/sys/kernel/mm/transparent_hugepage;/sys/devices/system/cpu/online;/sys/devices/system/node;/sys/devices;/sys/firmware;/var/lib/libvirt/qemu;/sys/fs/cgroup ++ ++[Readdir] ++Path=/proc;/sys/bus;/sys/kernel/iommu_groups;/sys/kernel/mm/hugepages;/sys/class;/sys/bus;/sys/class;/sys/devices/system;/var/lib/libvirt;/sys/fs/cgroup;/root/test;/sys/devices/system/node;/dev/pts;/home/VMs ++ ++[Mkdir] ++Path=/var/lib/libvirt/qemu;/home/VMs;/sys/fs/cgroup ++ ++[Rmdir] ++Path=/var/lib/libvirt/qemu;/home/VMs;/sys/fs/cgroup ++ ++[Create] ++Path=/var/lib/libvirt/qemu;/home/VMs;/sys/fs/cgroup ++ ++[Unlink] ++Path=/var/lib/libvirt/qemu;/home/VMs;/sys/fs/cgroup ++ ++[Rename] ++Path=/var/lib/libvirt/qemu;/home/VMs;/sys/fs/cgroup ++ ++[Setattr] ++Path=/sys/bus/pci/drivers/pcieport/unbind;/sys/bus/pci/drivers_probe;/sys/devices/pci0000:00/0000:00:08.0/driver_override;/root/test;/var/lib/libvirt/qemu;/sys/fs/cgroup;/home/VMs ++ ++[Setxattr] ++Path=/sys/bus/pci/drivers/pcieport/unbind;/sys/bus/pci/drivers_probe;/sys/devices/pci0000:00/0000:00:08.0/driver_override;/root/test;/var/lib/libvirt/qemu;/sys/fs/cgroup;/home/VMs ++ ++[Mount] ++Path=/home/VMs;/var/lib/libvirt;/proc;/sys;/dev/pts;/dev/vfio +\ No newline at end of file +-- +2.33.0 + diff --git a/0006-Fix-error-of-getxattr-and-listxattr.patch b/0006-Fix-error-of-getxattr-and-listxattr.patch new file mode 100644 index 0000000..913429e --- /dev/null +++ b/0006-Fix-error-of-getxattr-and-listxattr.patch @@ -0,0 +1,354 @@ +From e9615d46a09a5dc92bf1d2ee408f0c7efd717503 Mon Sep 17 00:00:00 2001 +From: yangxin <245051644@qq.com> +Date: Fri, 10 Feb 2023 16:39:20 +0800 +Subject: [PATCH 2/5] Fix-error-of-getxattr-and-listxattr + +Signed-off-by: yangxin <245051644@qq.com> +--- + qtfs/conn.c | 4 --- + qtfs/qtfs/qtfs-mod.c | 2 +- + qtfs/qtfs/sb.c | 6 ++--- + qtfs/qtfs/xattr.c | 55 ++++++++++------------------------------ + qtfs/qtfs_server/fsops.c | 16 +++++++----- + qtfs/qtinfo/qtinfo.h | 10 ++++---- + qtfs/req.h | 4 +-- + 7 files changed, 34 insertions(+), 63 deletions(-) + +diff --git a/qtfs/conn.c b/qtfs/conn.c +index af11fbe..26930b1 100644 +--- a/qtfs/conn.c ++++ b/qtfs/conn.c +@@ -76,10 +76,6 @@ static int qtfs_conn_sockserver_init(struct qtfs_sock_var_s *pvar) + { + struct socket *sock; + int ret; +- struct sockaddr_in saddr; +- saddr.sin_family = AF_INET; +- saddr.sin_port = htons(pvar->port); +- saddr.sin_addr.s_addr = in_aton(pvar->addr); + + if (!QTCONN_IS_EPOLL_CONN(pvar) && qtfs_server_main_sock != NULL) { + qtfs_info("qtfs server main sock is %lx, valid or out-of-date?", (unsigned long)qtfs_server_main_sock); +diff --git a/qtfs/qtfs/qtfs-mod.c b/qtfs/qtfs/qtfs-mod.c +index 9ccf0ee..abd9443 100644 +--- a/qtfs/qtfs/qtfs-mod.c ++++ b/qtfs/qtfs/qtfs-mod.c +@@ -9,7 +9,7 @@ static struct file_system_type qtfs_fs_type = { + .owner = THIS_MODULE, + .name = QTFS_FSTYPE_NAME, + .mount = qtfs_fs_mount, +- .kill_sb = qtfs_kill_sb,//qtfs_kill_sb, ++ .kill_sb = qtfs_kill_sb, + }; + MODULE_ALIAS_FS("qtfs"); + +diff --git a/qtfs/qtfs/sb.c b/qtfs/qtfs/sb.c +index 9374cfb..7445fad 100644 +--- a/qtfs/qtfs/sb.c ++++ b/qtfs/qtfs/sb.c +@@ -18,7 +18,7 @@ + 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_sock_var_s *pvar = qtfs_conn_get_param(); +@@ -396,7 +396,6 @@ ssize_t qtfs_writeiter(struct kiocb *kio, struct iov_iter *iov) + leftlen -= wrbuflen; + } while (leftlen); + +- //if (qtfs_support_epoll(kio->ki_filp->f_inode->i_mode) || ) { + do { + struct inode *inode = kio->ki_filp->f_inode; + struct qtfs_inode_priv *priv = inode->i_private; +@@ -406,7 +405,6 @@ ssize_t qtfs_writeiter(struct kiocb *kio, struct iov_iter *iov) + 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); + } +- //qtfs_info("qtfs write iter fifo %s sync poll.", filp->f_path.dentry->d_iname); + } while (0); + qtfs_info("qtfs write %s over, leftlen:%lu.", filp->f_path.dentry->d_iname, leftlen); + qtfs_conn_put_param(pvar); +@@ -1338,12 +1336,14 @@ static struct inode_operations qtfs_inode_ops = { + .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[] = { +diff --git a/qtfs/qtfs/xattr.c b/qtfs/qtfs/xattr.c +index a0d394a..a2a605d 100644 +--- a/qtfs/qtfs/xattr.c ++++ b/qtfs/qtfs/xattr.c +@@ -6,69 +6,52 @@ + #include "req.h" + #include "log.h" + +-static bool qtfs_xattr_list(struct dentry *dentry) ++ssize_t qtfs_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) + { + struct qtreq_xattrlist *req; + struct qtrsp_xattrlist *rsp; + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); +- bool ret; ++ ssize_t ret; + + if (!pvar) { + qtfs_err("qtfs_xattr_list Failed to get qtfs sock var"); +- return -EINVAL; ++ return 0; + } + + if (dentry == NULL) { + qtfs_err("qtfs_xattr_list dentry is NULL."); + qtfs_conn_put_param(pvar); +- return false; ++ return 0; + } + + req = qtfs_sock_msg_buf(pvar, QTFS_SEND); + if (qtfs_fullname(req->path, dentry) < 0) { + qtfs_err("qtfs fullname failed"); + qtfs_conn_put_param(pvar); +- return false; ++ return 0; + } +- +- rsp = qtfs_remote_run(pvar, QTFS_REQ_XATTRLIST, strlen(req->path) + 1); ++ req->buffer_size = buffer_size; ++ rsp = qtfs_remote_run(pvar, QTFS_REQ_XATTRLIST, QTFS_SEND_SIZE(struct qtreq_xattrlist, req->path)); + if (IS_ERR(rsp) || rsp == NULL) { + qtfs_err("qtfs_xattr_list remote run failed."); + qtfs_conn_put_param(pvar); +- return false; ++ return 0; + } + + if (rsp->d.ret == QTFS_ERR) { + qtfs_err("qtfs_xattr_list failed with ret:%d.", rsp->d.ret); +- ret = rsp->d.result; ++ ret = rsp->d.size; + qtfs_conn_put_param(pvar); + return ret; + } +- ret = rsp->d.result; ++ ret = rsp->d.size; ++ if (buffer != NULL) { ++ memcpy(buffer, rsp->name, buffer_size); ++ } + qtfs_conn_put_param(pvar); + return ret; + } + +-static bool qtfs_xattr_user_list(struct dentry *dentry) +-{ +- return qtfs_xattr_list(dentry); +-} +- +-static bool qtfs_xattr_trusted_list(struct dentry *dentry) +-{ +- return qtfs_xattr_list(dentry); +-} +- +-static bool qtfs_xattr_security_list(struct dentry *dentry) +-{ +- return qtfs_xattr_list(dentry); +-} +- +-static bool qtfs_xattr_hurd_list(struct dentry *dentry) +-{ +- return qtfs_xattr_list(dentry); +-} +- + static int qtfs_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, +@@ -177,12 +160,6 @@ static int qtfs_xattr_get(const struct xattr_handler *handler, + qtfs_err("Failed to get qtfs sock var"); + return 0; + } +- /*if (buf == NULL || size <= 0) { +- qtfs_err("xattr get failed, buf:%lx size:%d name:%s dentry:%lx", +- (unsigned long)buf, size, (name == NULL) ? "NULL" : name, (unsigned long)dentry); +- qtfs_conn_put_param(pvar); +- return 0; +- }*/ + + if (dentry == NULL) { + qtfs_err("xattr get dentry is NULL."); +@@ -216,7 +193,7 @@ static int qtfs_xattr_get(const struct xattr_handler *handler, + qtfs_conn_put_param(pvar); + return PTR_ERR(rsp); + } +- if (rsp->d.ret == QTFS_ERR || rsp->d.size > req->d.size || leftlen < rsp->d.size) { ++ if (rsp->d.ret == QTFS_ERR || (size !=0 && (rsp->d.size > req->d.size || leftlen < rsp->d.size))) { + qtfs_err("ret:%d rsp size:%ld req size:%d leftlen:%lu", rsp->d.ret, rsp->d.size, + req->d.size, leftlen); + goto err_end; +@@ -239,28 +216,24 @@ err_end: + + const struct xattr_handler qtfs_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, +- .list = qtfs_xattr_user_list, + .get = qtfs_xattr_get, + .set = qtfs_xattr_user_set, + }; + + const struct xattr_handler qtfs_xattr_trusted_handler = { + .prefix = XATTR_TRUSTED_PREFIX, +- .list = qtfs_xattr_trusted_list, + .get = qtfs_xattr_get, + .set = qtfs_xattr_trusted_set, + }; + + const struct xattr_handler qtfs_xattr_security_handler = { + .prefix = XATTR_SECURITY_PREFIX, +- .list = qtfs_xattr_security_list, + .get = qtfs_xattr_get, + .set = qtfs_xattr_security_set, + }; + + const struct xattr_handler qtfs_xattr_hurd_handler = { + .prefix = XATTR_HURD_PREFIX, +- .list = qtfs_xattr_hurd_list, + .get = qtfs_xattr_get, + .set = qtfs_xattr_hurd_set, + }; +diff --git a/qtfs/qtfs_server/fsops.c b/qtfs/qtfs_server/fsops.c +index d00db6d..61e8895 100644 +--- a/qtfs/qtfs_server/fsops.c ++++ b/qtfs/qtfs_server/fsops.c +@@ -576,6 +576,7 @@ static int handle_getattr(struct qtserver_arg *arg) + 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; + } +@@ -866,26 +867,27 @@ int handle_xattrlist(struct qtserver_arg *arg) + struct qtrsp_xattrlist *rsp = (struct qtrsp_xattrlist *)RSP(arg); + struct path path; + int ret; +- ssize_t size; ++ ssize_t size, buffer_size; + int i; + ++ buffer_size = req->buffer_size; + ret = kern_path(req->path, 0, &path); + if (ret) { + qtfs_err("handle xattr list path error.\n"); +- rsp->d.errno = -ENOENT; ++ rsp->d.size = -ENOENT; + goto err_handle; + } +- size = generic_listxattr(path.dentry, rsp->name, sizeof(rsp->name)); ++ 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.errno = size; ++ rsp->d.size = size; + goto err_handle; + } + if (size == 0) + goto err_handle; + rsp->d.ret = QTFS_OK; +- rsp->d.result = true; ++ rsp->d.size = size; + while (i < size) { + qtfs_info("handle list xattr result:%s\n", &rsp->name[i]); + i += strlen(&rsp->name[i]) + 1; +@@ -894,7 +896,7 @@ int handle_xattrlist(struct qtserver_arg *arg) + + err_handle: + rsp->d.ret = QTFS_ERR; +- rsp->d.result = false; ++ rsp->d.size = size; + return sizeof(struct qtrsp_xattrlist); + } + +@@ -970,9 +972,9 @@ int handle_xattrget(struct qtserver_arg *arg) + } + 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.size = len; + } + rsp->d.pos = req->d.pos + len; + } else { +diff --git a/qtfs/qtinfo/qtinfo.h b/qtfs/qtinfo/qtinfo.h +index 0244a6e..eb7e8be 100644 +--- a/qtfs/qtinfo/qtinfo.h ++++ b/qtfs/qtinfo/qtinfo.h +@@ -9,29 +9,29 @@ enum qtfs_req_type + QTFS_REQ_OPEN, + QTFS_REQ_CLOSE, + QTFS_REQ_READ, +- QTFS_REQ_READITER, //5 ++ QTFS_REQ_READITER, // 5 + QTFS_REQ_WRITE, + QTFS_REQ_LOOKUP, + QTFS_REQ_READDIR, + QTFS_REQ_MKDIR, +- QTFS_REQ_RMDIR, //10 ++ QTFS_REQ_RMDIR, // 10 + QTFS_REQ_GETATTR, + QTFS_REQ_SETATTR, + QTFS_REQ_ICREATE, + QTFS_REQ_MKNOD, +- QTFS_REQ_UNLINK, //15 ++ QTFS_REQ_UNLINK, // 15 + QTFS_REQ_SYMLINK, + QTFS_REQ_LINK, + QTFS_REQ_GETLINK, + QTFS_REQ_READLINK, +- QTFS_REQ_RENAME, //20 ++ QTFS_REQ_RENAME, // 20 + + QTFS_REQ_XATTRLIST, + QTFS_REQ_XATTRGET, + QTFS_REQ_XATTRSET, + + QTFS_REQ_SYSMOUNT, +- QTFS_REQ_SYSUMOUNT, //25 ++ QTFS_REQ_SYSUMOUNT, // 25 + QTFS_REQ_FIFOPOLL, + + QTFS_REQ_STATFS, +diff --git a/qtfs/req.h b/qtfs/req.h +index 3bcfa77..29f8964 100644 +--- a/qtfs/req.h ++++ b/qtfs/req.h +@@ -414,14 +414,14 @@ struct qtrsp_rename { + // xattr def + #define QTFS_XATTR_LEN 64 + struct qtreq_xattrlist { ++ size_t buffer_size; + char path[MAX_PATH_LEN]; + }; + + struct qtrsp_xattrlist { + struct qtrsp_xattrlist_len { + int ret; +- int errno; +- bool result; ++ ssize_t size; + }d; + char name[QTFS_TAIL_LEN(struct qtrsp_xattrlist_len)]; + }; +-- +2.33.0 + diff --git a/0007-Add-whitelist-of-rexec.patch b/0007-Add-whitelist-of-rexec.patch new file mode 100644 index 0000000..7dfc5c8 --- /dev/null +++ b/0007-Add-whitelist-of-rexec.patch @@ -0,0 +1,197 @@ +From 92d4368180a81bc4220449f5be6123a1aa32417b Mon Sep 17 00:00:00 2001 +From: yangxin <245051644@qq.com> +Date: Fri, 10 Feb 2023 16:56:58 +0800 +Subject: [PATCH 3/5] Add whitelist of rexec + +Signed-off-by: yangxin <245051644@qq.com> +--- + qtfs/rexec/client.go | 4 +- + qtfs/rexec/common.go | 29 ++++++++++++++ + qtfs/rexec/server.go | 38 ++++++++++++++++++- + .../whitelist/libvirt/rexec_whitelist | 4 ++ + 4 files changed, 72 insertions(+), 3 deletions(-) + create mode 100644 usecases/transparent-offload/whitelist/libvirt/rexec_whitelist + +diff --git a/qtfs/rexec/client.go b/qtfs/rexec/client.go +index 13b63f5..dc1af8b 100644 +--- a/qtfs/rexec/client.go ++++ b/qtfs/rexec/client.go +@@ -156,7 +156,6 @@ func main() { + + retryCnt := 3 + // 1. get pid from response +- time.Sleep(5 * time.Millisecond) + response := &CommandResponse{} + retry: + err = receiver.Receive(response) +@@ -168,6 +167,9 @@ retry: + } + log.Fatal(err) + } ++ if (response.WhiteList == 0) { ++ log.Fatalf("%s command in White List of rexec server\n", command.Cmd) ++ } + pid := response.Pid + lpid := os.Getpid() + log.Printf("create pidFile for %d:%d\n", pid, lpid) +diff --git a/qtfs/rexec/common.go b/qtfs/rexec/common.go +index 9ce21c4..b59b12b 100644 +--- a/qtfs/rexec/common.go ++++ b/qtfs/rexec/common.go +@@ -8,6 +8,7 @@ import ( + "os" + "strconv" + "strings" ++ "syscall" + "io/ioutil" + "encoding/json" + +@@ -30,10 +31,34 @@ type RemoteCommand struct { + Cgroups map[string]string + } + ++func CheckRight(fileName string) error { ++ var uid int ++ var gid int ++ var mode int ++ var stat syscall.Stat_t ++ if err := syscall.Stat(fileName, &stat); err != nil { ++ return fmt.Errorf("Can't get status of %s: %s\n", fileName, err) ++ } ++ uid = int(stat.Uid) ++ gid = int(stat.Gid) ++ mode = int(stat.Mode) ++ ++ if (uid != 0 || gid != 0) { ++ return fmt.Errorf("Owner of %s must be root\n", fileName) ++ } ++ ++ if (mode & 0777 != 0400) { ++ return fmt.Errorf("Mode of %s must be 0400\n", fileName) ++ } ++ ++ return nil ++} ++ + // CommandResponse is the returned response object from the remote execution + type CommandResponse struct { + Pid int + Status int ++ WhiteList int + } + + // NetAddr is struct to describe net proto and addr +@@ -90,6 +115,10 @@ func parseUnixAddr(inAddr string) (NetAddr, error) { + + func readAddrFromFile(role string) (string) { + fileName := fmt.Sprintf("%s/%s.json", configDir, role) ++ if err := CheckRight(fileName); err != nil { ++ fmt.Printf("Check right of %s failed: %s", fileName, err) ++ return "" ++ } + file, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("read %s failed: %s", fileName, err) +diff --git a/qtfs/rexec/server.go b/qtfs/rexec/server.go +index 4559b79..de3f6cf 100644 +--- a/qtfs/rexec/server.go ++++ b/qtfs/rexec/server.go +@@ -4,6 +4,7 @@ import ( + "crypto/tls" + "fmt" + "io" ++ "io/ioutil" + "log" + "net" + "os" +@@ -17,13 +18,33 @@ import ( + + const ( + role = "server" ++ whiteList = "whitelist" + ) ++var WhiteLists map[string] int ++func getWhitelist() error { ++ fileName := fmt.Sprintf("%s/%s", configDir, whiteList) ++ if err := CheckRight(fileName); err != nil { ++ log.Fatal(err) ++ } ++ file, err := ioutil.ReadFile(fileName) ++ if err != nil { ++ fmt.Printf("read %s failed: %s", fileName, err) ++ return err ++ } ++ fileContent := string(file) ++ lines := strings.Split(fileContent, "\n") ++ for i, v := range lines { ++ WhiteLists[v] = i ++ } ++ return nil ++} + + func getHost(addr string) string { + return strings.Split(addr, ":")[0] + } + + func main() { ++ WhiteLists = make(map[string]int, 10) + cert := os.Getenv("TLS_CERT") + key := os.Getenv("TLS_KEY") + +@@ -32,6 +53,10 @@ func main() { + if err != nil { + log.Fatal(err) + } ++ if err := getWhitelist(); err != nil { ++ log.Println("Get Whitelist failed") ++ return ++ } + if cert != "" && key != "" { + tlsCert, err := tls.LoadX509KeyPair(cert, key) + if err != nil { +@@ -86,13 +111,23 @@ func main() { + } + + command := &RemoteCommand{} ++ returnResult := &CommandResponse{} ++ returnResult.WhiteList = 1 + err = receiver.Receive(command) + if err != nil { + log.Print(err) + return + } + log.Printf("cmd(%s), args(%v)\n", command.Cmd, command.Args) +- ++ if _, ok := WhiteLists[command.Cmd]; !ok { ++ log.Printf("%s not in WhiteLists", command.Cmd) ++ returnResult.WhiteList = 0 ++ err = command.StatusChan.Send(returnResult) ++ if err != nil { ++ log.Print(err) ++ } ++ return ++ } + cmd := exec.Command(command.Cmd, command.Args...) + cmd.Stdout = command.Stdout + cmd.Stderr = command.Stderr +@@ -111,7 +146,6 @@ func main() { + defer command.Stdout.Close() + defer command.Stderr.Close() + +- returnResult := &CommandResponse{} + err = cmd.Start() + if err != nil { + // send return status back +diff --git a/usecases/transparent-offload/whitelist/libvirt/rexec_whitelist b/usecases/transparent-offload/whitelist/libvirt/rexec_whitelist +new file mode 100644 +index 0000000..275a3e5 +--- /dev/null ++++ b/usecases/transparent-offload/whitelist/libvirt/rexec_whitelist +@@ -0,0 +1,4 @@ ++/usr/bin/qemu-kvm ++taskset ++kill ++/usr/bin/kill +-- +2.33.0 + diff --git a/0008-Add-udsproxy.patch b/0008-Add-udsproxy.patch new file mode 100644 index 0000000..6ccc3b0 --- /dev/null +++ b/0008-Add-udsproxy.patch @@ -0,0 +1,2812 @@ +From 435916d197bfce059e3afbca0975315fc5b1662f Mon Sep 17 00:00:00 2001 +From: yangxin <245051644@qq.com> +Date: Fri, 10 Feb 2023 17:02:05 +0800 +Subject: [PATCH 4/5] Add udsproxy. + +Signed-off-by: yangxin <245051644@qq.com> +--- + qtfs/README.md | 7 +- + qtfs/comm.h | 8 +- + qtfs/conn.c | 9 +- + qtfs/conn.h | 1 + + qtfs/ipc/Makefile | 22 + + qtfs/ipc/uds_connector.c | 129 +++++ + qtfs/ipc/uds_event.c | 999 +++++++++++++++++++++++++++++++++ + qtfs/ipc/uds_event.h | 64 +++ + qtfs/ipc/uds_main.c | 556 ++++++++++++++++++ + qtfs/ipc/uds_main.h | 141 +++++ + qtfs/ipc/uds_module.h | 19 + + qtfs/misc.c | 7 + + qtfs/qtfs/sb.c | 6 +- + qtfs/qtfs_server/Makefile | 17 +- + qtfs/qtfs_server/fsops.c | 5 +- + qtfs/qtfs_server/qtfs-server.c | 11 +- + qtfs/qtfs_server/user_engine.c | 26 +- + qtfs/qtinfo/qtinfo.c | 77 ++- + qtfs/qtsock.c | 332 +++++++++++ + 19 files changed, 2406 insertions(+), 30 deletions(-) + create mode 100644 qtfs/ipc/Makefile + create mode 100644 qtfs/ipc/uds_connector.c + create mode 100644 qtfs/ipc/uds_event.c + create mode 100644 qtfs/ipc/uds_event.h + create mode 100644 qtfs/ipc/uds_main.c + create mode 100644 qtfs/ipc/uds_main.h + create mode 100644 qtfs/ipc/uds_module.h + create mode 100644 qtfs/qtsock.c + +diff --git a/qtfs/README.md b/qtfs/README.md +index 0cbc2e1..19987e0 100644 +--- a/qtfs/README.md ++++ b/qtfs/README.md +@@ -24,6 +24,7 @@ qtfs的特性: + ## 安装教程 + + 目录说明: +++ **ipc**: 跨主机unix domain socket协同组件,在该目录下编译udsproxyd二进制和libudsproxy.so库。 + + **qtfs**: 客户端内核模块相关代码,直接在该目录下编译客户端ko。 + + **qtfs_server**: 服务端内核模块相关代码,直接在该目录下编译服务端ko和相关程序。 + + **qtinfo**: 诊断工具,支持查询文件系统的工作状态以及修改log级别等。 +@@ -34,19 +35,23 @@ qtfs的特性: + + 1. 要求内核版本在5.10或更高版本。 + 2. 安装内核开发包:yum install kernel-devel。 ++ 3. 假设host服务器ip为192.168.10.10,dpu为192.168.10.11 + + 服务端安装: + + 1. cd qtfs_server + 2. make clean && make + 3. insmod qtfs_server.ko qtfs_server_ip=x.x.x.x qtfs_server_port=12345 qtfs_log_level=WARN +- 4. ./engine 4096 16 ++ 4. nohup ./engine 16 1 192.168.10.10 12121 192.168.10.11 12121 2>&1 & + + 客户端安装: + + 1. cd qtfs + 2. make clean && make + 3. insmod qtfs.ko qtfs_server_ip=x.x.x.x qtfs_server_port=12345 qtfs_log_level=WARN ++ 4. cd ../ipc/ ++ 5. make clean && make && make install ++ 6. nohup udsproxyd 1 192.168.10.11 12121 192.168.10.10 12121 2>&1 & + + ## 使用说明 + +diff --git a/qtfs/comm.h b/qtfs/comm.h +index 901552c..2e562bb 100644 +--- a/qtfs/comm.h ++++ b/qtfs/comm.h +@@ -3,6 +3,9 @@ + + extern struct qtinfo *qtfs_diag_info; + ++#define QTFS_CLIENT_DEV "/dev/qtfs_client" ++#define QTFS_SERVER_DEV "/dev/qtfs_server" ++ + #define QTFS_IOCTL_MAGIC 'Q' + enum { + _QTFS_IOCTL_EXEC, +@@ -18,6 +21,7 @@ enum { + + _QTFS_IOCTL_LOG_LEVEL, + _QTFS_IOCTL_EPOLL_SUPPORT, ++ _QTFS_IOCTL_UDS_PROXY_PID, + }; + + #define QTFS_IOCTL_THREAD_INIT _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_EXEC) +@@ -31,6 +35,7 @@ enum { + #define QTFS_IOCTL_CLEARALL _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_CLEARALL) + #define QTFS_IOCTL_LOGLEVEL _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_LOG_LEVEL) + #define QTFS_IOCTL_EPOLL_SUPPORT _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_EPOLL_SUPPORT) ++#define QTFS_IOCTL_UDS_PROXY_PID _IO(QTFS_IOCTL_MAGIC, _QTFS_IOCTL_UDS_PROXY_PID) + + #define QTINFO_MAX_EVENT_TYPE 36 // look qtreq_type at req.h + #define QTFS_FUNCTION_LEN 64 +@@ -119,6 +124,7 @@ enum qtinfo_cnts { + }; + #endif + ++#if (defined(QTFS_CLIENT) || defined(client) || defined(QTFS_SERVER) || defined(server)) + // for connection state machine + typedef enum { + QTCONN_INIT, +@@ -159,7 +165,7 @@ struct qtinfo { + #define QTINFO_STATE(state) ((state == QTCONN_INIT) ? "INIT" : \ + ((state == QTCONN_CONNECTING) ? "CONNECTING" : \ + ((state == QTCONN_ACTIVE) ? "ACTIVE" : "UNKNOWN"))) +- ++#endif + //ko compile + #if (defined(QTFS_CLIENT) || defined(client)) + static inline void qtinfo_clear(void) +diff --git a/qtfs/conn.c b/qtfs/conn.c +index 26930b1..c84c85c 100644 +--- a/qtfs/conn.c ++++ b/qtfs/conn.c +@@ -32,6 +32,7 @@ struct qtfs_sock_var_s *qtfs_epoll_var = NULL; + struct socket *qtfs_server_main_sock = NULL; + struct qtfs_server_userp_s *qtfs_userps = NULL; + #endif ++int qtfs_uds_proxy_pid = -1; + #define QTFS_EPOLL_THREADIDX (QTFS_MAX_THREADS + 4) + + +@@ -76,6 +77,10 @@ static int qtfs_conn_sockserver_init(struct qtfs_sock_var_s *pvar) + { + struct socket *sock; + int ret; ++ struct sockaddr_in saddr; ++ saddr.sin_family = AF_INET; ++ saddr.sin_port = htons(pvar->port); ++ saddr.sin_addr.s_addr = in_aton(pvar->addr); + + if (!QTCONN_IS_EPOLL_CONN(pvar) && qtfs_server_main_sock != NULL) { + qtfs_info("qtfs server main sock is %lx, valid or out-of-date?", (unsigned long)qtfs_server_main_sock); +@@ -147,10 +152,6 @@ static int qtfs_conn_sockclient_init(struct qtfs_sock_var_s *pvar) + { + struct socket *sock; + int ret; +- struct sockaddr_in saddr; +- saddr.sin_family = AF_INET; +- saddr.sin_port = htons(pvar->port); +- saddr.sin_addr.s_addr = in_aton(pvar->addr); + + ret = sock_create_kern(&init_net, AF_INET, SOCK_STREAM, 0, &sock); + if (ret) { +diff --git a/qtfs/conn.h b/qtfs/conn.h +index db590fc..742def4 100644 +--- a/qtfs/conn.h ++++ b/qtfs/conn.h +@@ -26,6 +26,7 @@ extern char qtfs_log_level[QTFS_LOGLEVEL_STRLEN]; + extern int log_level; + extern struct qtinfo *qtfs_diag_info; + extern bool qtfs_epoll_mode; ++extern int qtfs_uds_proxy_pid; + + #define qtfs_conn_get_param(void) _qtfs_conn_get_param(__func__) + +diff --git a/qtfs/ipc/Makefile b/qtfs/ipc/Makefile +new file mode 100644 +index 0000000..47e74ad +--- /dev/null ++++ b/qtfs/ipc/Makefile +@@ -0,0 +1,22 @@ ++all: udsproxyd libudsproxy.so ++ ++udsproxyd: uds_event.o uds_main.o ++ gcc -g -O2 -o udsproxyd $^ -I../ ++ ++uds_event.o: ++ cc -g -c -o uds_event.o uds_event.c ++ ++uds_main.o: ++ cc -g -c -o uds_main.o uds_main.c ++ ++libudsproxy.so: ++ gcc -g -O2 -o libudsproxy.so uds_connector.c -fPIC --shared ++ ++install: ++ yes | cp udsproxyd /usr/bin/ ++ yes | cp libudsproxy.so /usr/lib64/ ++ ++clean: ++ @rm -rf *.o udsproxyd libudsproxy.so ++ ++.PHONY: clean +diff --git a/qtfs/ipc/uds_connector.c b/qtfs/ipc/uds_connector.c +new file mode 100644 +index 0000000..3c46ce5 +--- /dev/null ++++ b/qtfs/ipc/uds_connector.c +@@ -0,0 +1,129 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "uds_module.h" ++ ++#define uds_log(info, ...) \ ++ do { \ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } while (0); ++ ++#define uds_err(info, ...) \ ++ do { \ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } while (0); ++ ++static unsigned short uds_conn_get_sock_type(int sockfd) ++{ ++ unsigned short type; ++ int len = 2; ++ int ret = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &type, &len); ++ if (ret < 0) { ++ uds_err("get sock type failed, fd:%d", sockfd); ++ return (unsigned short)-1; ++ } ++ uds_log("fd:%d type:%d", sockfd, type); ++ return type; ++} ++ ++static int uds_conn_whitelist_check(const char *path) ++{ ++ return 1; ++} ++ ++int connect(int fd, const struct sockaddr *addrarg, socklen_t len) ++{ ++ int sock_fd; ++ typeof(connect) *libcconnect = NULL; ++ int libcret; ++ const struct sockaddr_un *addr = (const struct sockaddr_un *)addrarg; ++ ++ if (libcconnect == NULL) { ++ libcconnect = dlsym(((void *) - 1l), "connect"); ++ if (libcconnect == NULL) { ++ uds_err("can't find connect by dlsym."); ++ return -1; ++ } ++ } ++ ++ libcret = (*libcconnect)(fd, addrarg, len); ++ if (libcret == 0 || addr->sun_family != AF_UNIX) { ++ // 如果本地connect成功,或者非UNIX DOMAIN SOCKET,都直接返回即可 ++ return libcret; ++ } ++ ++ uds_log("enter uds connect fd:%d sunpath:%s family:%d len:%d connect function:0x%lx", fd, addr->sun_path, ++ addr->sun_family, len, libcconnect); ++ // 本地未连接,且是uds链接 ++ if (!uds_conn_whitelist_check(addr->sun_path)) { ++ uds_err("path:%s not in white list", addr->sun_path); ++ return libcret; ++ } ++ ++ // 尝试远端链接 ++ do { ++ int ret; ++ struct uds_proxy_remote_conn_req remoteconn; ++ struct uds_proxy_remote_conn_rsp remotersp; ++ struct sockaddr_un proxy = {.sun_family = AF_UNIX}; ++ sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (sock_fd < 0) { ++ uds_err("create socket failed"); ++ return libcret; ++ } ++ ++ strncpy(proxy.sun_path, UDS_BUILD_CONN_ADDR, sizeof(proxy.sun_path)); ++ if ((*libcconnect)(sock_fd, (struct sockaddr *)&proxy, sizeof(struct sockaddr_un)) < 0) { ++ uds_err("can't connect to uds proxy: %s", UDS_BUILD_CONN_ADDR); ++ goto err_end; ++ } ++ // 这里type需要是第一个入参fd的type ++ remoteconn.type = uds_conn_get_sock_type(fd); ++ if (remoteconn.type == (unsigned short)-1) { ++ remoteconn.type = SOCK_STREAM; ++ } ++ memset(remoteconn.sun_path, 0, sizeof(remoteconn.sun_path)); ++ strncpy(remoteconn.sun_path, addr->sun_path, sizeof(remoteconn.sun_path)); ++ ret = send(sock_fd, &remoteconn, sizeof(remoteconn), 0); ++ if (ret <= 0) { ++ uds_err("send remote connect request failed, ret:%d err:%s", ret, strerror(errno)); ++ goto err_end; ++ } ++ ret = recv(sock_fd, &remotersp, sizeof(remotersp), MSG_WAITALL); ++ if (ret <= 0) { ++ uds_err("recv remote connect replay failed, ret:%d err:%s", ret, strerror(errno)); ++ goto err_end; ++ } ++ if (remotersp.ret == 0) { ++ goto err_end; ++ } ++ } while(0); ++ ++ close(sock_fd); ++ return (*libcconnect)(fd, addrarg, len); ++ ++err_end: ++ close(sock_fd); ++ return libcret; ++} +diff --git a/qtfs/ipc/uds_event.c b/qtfs/ipc/uds_event.c +new file mode 100644 +index 0000000..ff4d79b +--- /dev/null ++++ b/qtfs/ipc/uds_event.c +@@ -0,0 +1,999 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dirent.h" ++ ++#include "uds_main.h" ++#include "uds_event.h" ++ ++int uds_event_build_step2(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_remote_build(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_build_step3(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_uds2tcp(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_tcp2uds(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_build_step4(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_tcp2pipe(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_pipe2tcp(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++ ++int uds_event_module_init(struct uds_event_global_var *p) ++{ ++ p->msg_controllen = UDS_EVENT_BUFLEN; ++ p->iov_len = UDS_EVENT_BUFLEN; ++ p->buflen = UDS_EVENT_BUFLEN; ++ p->msg_controlsendlen = UDS_EVENT_BUFLEN; ++ p->iov_sendlen = UDS_EVENT_BUFLEN; ++ ++ p->msg_control = (char *)malloc(p->msg_controllen); ++ if (p->msg_control == NULL) { ++ uds_err("malloc msg control buf failed."); ++ p->msg_controllen = 0; ++ return EVENT_ERR; ++ } ++ p->msg_control_send = (char *)malloc(p->msg_controlsendlen); ++ if (p->msg_control_send == NULL) { ++ goto free1; ++ } ++ p->iov_base = (char *)malloc(p->iov_len); ++ if (p->iov_base == NULL) { ++ uds_err("malloc iov base failed."); ++ goto free2; ++ } ++ p->iov_base_send = (char *)malloc(p->iov_sendlen); ++ if (p->iov_base_send == NULL) { ++ goto free3; ++ } ++ p->buf = (char *)malloc(p->buflen); ++ if (p->buf == NULL) { ++ uds_err("malloc buf failed."); ++ goto free4; ++ } ++ return EVENT_OK; ++ ++free4: ++ free(p->iov_base_send); ++ p->iov_base_send = NULL; ++ ++free3: ++ free(p->iov_base); ++ p->iov_base = NULL; ++ ++free2: ++ free(p->msg_control_send); ++ p->msg_control_send = NULL; ++ ++free1: ++ free(p->msg_control); ++ p->msg_control = NULL; ++ return EVENT_ERR; ++} ++ ++void uds_event_module_fini(struct uds_event_global_var *p) ++{ ++ if (p->msg_control != NULL) { ++ free(p->msg_control); ++ p->msg_control = NULL; ++ p->msg_controllen = 0; ++ } ++ if (p->msg_control_send != NULL) { ++ free(p->msg_control_send); ++ p->msg_control_send = NULL; ++ p->msg_controlsendlen = 0; ++ } ++ if (p->iov_base != NULL) { ++ free(p->iov_base); ++ p->iov_base = NULL; ++ p->iov_len = 0; ++ } ++ if (p->iov_base_send != NULL) { ++ free(p->iov_base_send); ++ p->iov_base_send = NULL; ++ p->iov_sendlen = 0; ++ } ++ if (p->buf != NULL) { ++ free(p->buf); ++ p->buf = NULL; ++ p->buflen = 0; ++ } ++ return; ++} ++ ++int uds_event_pre_hook(struct uds_event_global_var *p_event_var) ++{ ++ p_event_var->cur = 0; ++ memset(p_event_var->tofree, 0, sizeof(struct uds_event *) * UDS_EPOLL_MAX_EVENTS); ++ return 0; ++} ++ ++int uds_event_post_hook(struct uds_event_global_var *p_event_var) ++{ ++ for (int i = 0; i < p_event_var->cur; i++) { ++ uds_log("event:%lx fd:%d free by its peer", p_event_var->tofree[i], p_event_var->tofree[i]->fd); ++ uds_del_event(p_event_var->tofree[i]); ++ } ++ return 0; ++} ++ ++int uds_event_add_to_free(struct uds_event_global_var *p_event_var, struct uds_event *evt) ++{ ++ if (evt->pipe == 1) { ++ uds_log("pipe event:%d no need to free peer", evt->fd); ++ return 0; ++ } ++ ++ struct uds_event *peerevt = evt->peer; ++ if (peerevt == NULL) { ++ uds_err("peer event add to free is NULL, my fd:%d", evt->fd); ++ return -1; ++ } ++ peerevt->tofree = 1; ++ uds_log("event fd:%d addr:%lx add to free", peerevt->fd, peerevt); ++ p_event_var->tofree[p_event_var->cur] = peerevt; ++ p_event_var->cur++; ++ return 0; ++} ++ ++int uds_event_pre_handler(struct uds_event *evt) ++{ ++ if (evt->tofree == 1) { ++ uds_log("event fd:%d marked by peer as pending deletion", evt->fd); ++ return EVENT_ERR; ++ } ++ return EVENT_OK; ++} ++ ++/* ++ * 1. accept local uds connect request ++ * 2. set new connection's event to build link step2 ++ * 3. add new connection event to epoll list ++ */ ++int uds_event_uds_listener(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ int connfd; ++ struct uds_event *evt = (struct uds_event *)arg; ++ if (evt == NULL) { ++ uds_err("param is invalid."); ++ return EVENT_ERR; ++ } ++ connfd = uds_sock_step_accept(evt->fd, AF_UNIX); ++ if (connfd <= 0) { ++ uds_err("conn fd error:%d", connfd); ++ return EVENT_ERR; ++ } ++ ++ uds_log("accept an new connection, fd:%d", connfd); ++ ++ uds_add_event(connfd, NULL, uds_event_build_step2, NULL); ++ return EVENT_OK; ++} ++ ++int uds_event_build_step2(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ if (evt == NULL) { ++ uds_err("param is invalid."); ++ return EVENT_ERR; ++ } ++ char buf[sizeof(struct uds_tcp2tcp) + sizeof(struct uds_proxy_remote_conn_req)] = {0}; ++ struct uds_tcp2tcp *bdmsg = (struct uds_tcp2tcp *)buf; ++ struct uds_proxy_remote_conn_req *msg = (struct uds_proxy_remote_conn_req *)bdmsg->data; ++ int len; ++ memset(buf, 0, sizeof(buf)); ++ len = recv(evt->fd, msg, sizeof(struct uds_proxy_remote_conn_req), MSG_WAITALL); ++ if (len == 0) { ++ uds_err("recv err msg:%d errno:%s", len, strerror(errno)); ++ return EVENT_DEL; ++ } ++ if (len < 0) { ++ uds_err("read msg error:%d errno:%s", len, strerror(errno)); ++ goto end; ++ } ++ if (msg->type != SOCK_STREAM && msg->type != SOCK_DGRAM) { ++ uds_err("uds type:%d invalid", msg->type); ++ return EVENT_ERR; ++ } ++ ++ struct uds_conn_arg tcp = { ++ .cs = UDS_SOCKET_CLIENT, ++ }; ++ int ret; ++ if ((ret = uds_build_tcp_connection(&tcp)) < 0) { ++ uds_err("step2 build tcp connection failed, return:%d", ret); ++ goto end; ++ } ++ bdmsg->msgtype = MSGCNTL_UDS; ++ bdmsg->msglen = sizeof(struct uds_proxy_remote_conn_req); ++ if (write(tcp.connfd, bdmsg, sizeof(struct uds_tcp2tcp) + sizeof(struct uds_proxy_remote_conn_req)) < 0) { ++ uds_err("send msg to tcp failed"); ++ goto end; ++ } ++ ++ struct uds_proxy_remote_conn_req *priv = (void *)malloc(sizeof(struct uds_proxy_remote_conn_req)); ++ if (priv == NULL) { ++ uds_err("malloc failed"); ++ goto end; ++ } ++ ++ uds_log("step2 recv sun path:%s, add step3 event fd:%d", msg->sun_path, tcp.connfd); ++ memcpy(priv, msg, sizeof(struct uds_proxy_remote_conn_req)); ++ uds_add_event(tcp.connfd, evt, uds_event_build_step3, priv); ++ ++end: ++ return EVENT_OK; ++} ++ ++ ++int uds_event_build_step3(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ struct uds_proxy_remote_conn_rsp msg; ++ int len; ++ memset(&msg, 0, sizeof(struct uds_proxy_remote_conn_rsp)); ++ len = read(evt->fd, &msg, sizeof(struct uds_proxy_remote_conn_rsp)); ++ if (len <= 0) { ++ uds_err("read error len:%d", len); ++ if (len == 0) ++ goto event_del; ++ return EVENT_ERR; ++ } ++ if (msg.ret == EVENT_ERR) { ++ uds_log("get build ack:%d, failed", msg.ret); ++ goto event_del; ++ } ++ ++ struct uds_proxy_remote_conn_req *udsmsg = (struct uds_proxy_remote_conn_req *)evt->priv; ++ struct uds_conn_arg uds; ++ ++ memset(&uds, 0, sizeof(struct uds_conn_arg)); ++ uds.cs = UDS_SOCKET_SERVER; ++ uds.udstype = udsmsg->type; ++ strncpy(uds.sun_path, udsmsg->sun_path, sizeof(uds.sun_path)); ++ if (uds_build_unix_connection(&uds) < 0) { ++ uds_err("failed to build uds server sunpath:%s", uds.sun_path); ++ goto event_del; ++ } ++ uds_log("remote conn build success, build uds server type:%d sunpath:%s fd:%d OK this event suspend,", ++ udsmsg->type, udsmsg->sun_path, uds.sockfd); ++ uds_event_suspend(epfd, evt); ++ uds_add_event(uds.sockfd, evt, uds_event_build_step4, NULL); ++ ++ msg.ret = 1; ++ write(evt->peer->fd, &msg, sizeof(struct uds_proxy_remote_conn_rsp)); ++ return EVENT_OK; ++ ++event_del: ++ msg.ret = 0; ++ write(evt->peer->fd, &msg, sizeof(struct uds_proxy_remote_conn_rsp)); ++ free(evt->priv); ++ return EVENT_DEL; ++} ++ ++int uds_event_build_step4(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ int connfd = uds_sock_step_accept(evt->fd, AF_UNIX); ++ if (connfd < 0) { ++ uds_err("accept connection failed fd:%d", connfd); ++ return EVENT_ERR; ++ } ++ struct uds_event *peerevt = (struct uds_event *)evt->peer; ++ peerevt->handler = uds_event_tcp2uds; ++ peerevt->peer = uds_add_event(connfd, peerevt, uds_event_uds2tcp, NULL); ++ ++ uds_log("accept new connection fd:%d, peerfd:%d frontfd:%d peerfd:%d, peerevt(fd:%d) active now", ++ connfd, evt->peer->fd, peerevt->fd, peerevt->peer->fd, peerevt->fd); ++ uds_event_insert(epfd, peerevt); ++ return EVENT_DEL; ++} ++ ++int uds_event_tcp_listener(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ int connfd = uds_sock_step_accept(evt->fd, AF_INET); ++ if (connfd <= 0) { ++ uds_err("tcp conn fd error:%d", connfd); ++ return EVENT_ERR; ++ } ++ uds_log("tcp listener event enter, new connection fd:%d.", connfd); ++ ++ uds_add_event(connfd, NULL, uds_event_remote_build, NULL); ++ return 0; ++} ++ ++int uds_build_connect2uds(struct uds_event *evt, struct uds_proxy_remote_conn_req *msg) ++{ ++ struct uds_conn_arg targ; ++ int len = recv(evt->fd, msg, sizeof(struct uds_proxy_remote_conn_req), MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("recv failed, len:%d str:%s", len, strerror(errno)); ++ return EVENT_ERR; ++ } ++ ++ targ.cs = UDS_SOCKET_CLIENT; ++ targ.udstype = msg->type; ++ memset(targ.sun_path, 0, sizeof(targ.sun_path)); ++ strncpy(targ.sun_path, msg->sun_path, sizeof(targ.sun_path)); ++ if (uds_build_unix_connection(&targ) < 0) { ++ struct uds_proxy_remote_conn_rsp ack; ++ uds_err("can't connect to sun_path:%s", targ.sun_path); ++ ack.ret = EVENT_ERR; ++ write(evt->fd, &ack, sizeof(struct uds_proxy_remote_conn_rsp)); ++ return EVENT_DEL; ++ } ++ ++ evt->peer = uds_add_event(targ.connfd, evt, uds_event_uds2tcp, NULL); ++ evt->handler = uds_event_tcp2uds; ++ ++ uds_log("build link req from tcp, sunpath:%s, type:%d, eventfd:%d peerfd:%d", ++ msg->sun_path, msg->type, targ.connfd, evt->fd); ++ ++ struct uds_proxy_remote_conn_rsp ack; ++ ack.ret = EVENT_OK; ++ ++ int ret = write(evt->fd, &ack, sizeof(struct uds_proxy_remote_conn_rsp)); ++ if (ret <= 0) { ++ uds_err("apply ack failed, ret:%d", ret); ++ return EVENT_DEL; ++ } ++ return EVENT_OK; ++} ++ ++int uds_build_pipe_proxy(struct uds_event *evt, struct uds_stru_scm_pipe *msg) ++{ ++ int len = recv(evt->fd, msg, sizeof(struct uds_stru_scm_pipe), MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("recv failed, len:%d str:%s", len, strerror(errno)); ++ return EVENT_ERR; ++ } ++ if (msg->dir != SCM_PIPE_READ && msg->dir != SCM_PIPE_WRITE) { ++ uds_err("invalid pipe dir:%d", msg->dir); ++ return EVENT_ERR; ++ } ++ uds_log("pipe proxy event fd:%d pipe fd:%d dir:%d", evt->fd, msg->srcfd, msg->dir); ++ ++ if (msg->dir == SCM_PIPE_READ) { ++ evt->pipe = 1; ++ evt->peerfd = evt->fd; ++ evt->fd = msg->srcfd; ++ evt->handler = uds_event_pipe2tcp; ++ } else { ++ evt->pipe = 1; ++ evt->peerfd = msg->srcfd; ++ evt->handler = uds_event_tcp2pipe; ++ } ++ return EVENT_OK; ++} ++ ++int uds_event_remote_build(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ struct uds_tcp2tcp *bdmsg = (struct uds_tcp2tcp *)p_event_var->iov_base; ++ struct uds_proxy_remote_conn_req *msg = (struct uds_proxy_remote_conn_req *)bdmsg->data; ++ int len; ++ int ret = EVENT_OK; ++ memset(p_event_var->iov_base, 0, p_event_var->iov_len); ++ len = recv(evt->fd, bdmsg, sizeof(struct uds_tcp2tcp), MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("read no msg from sock:%d, len:%d", evt->fd, len); ++ return EVENT_ERR; ++ } ++ ++ switch (bdmsg->msgtype) { ++ case MSGCNTL_UDS: ++ ret = uds_build_connect2uds(evt, msg); ++ break; ++ case MSGCNTL_PIPE: ++ ret = uds_build_pipe_proxy(evt, (struct uds_stru_scm_pipe *)bdmsg->data); ++ break; ++ default: ++ uds_err("remote build not support msgtype %d now", bdmsg->msgtype); ++ break; ++ } ++ return ret; ++} ++ ++static inline mode_t uds_msg_file_mode(int fd) ++{ ++ struct stat st; ++ char path[32] = {0}; ++ if (fstat(fd, &st) != 0) { ++ uds_err("get fd:%d fstat failed, errstr:%s", fd, strerror(errno)); ++ } ++ if (S_ISFIFO(st.st_mode)) { ++ uds_log("fd:%d is fifo", fd); ++ } ++ ++ return st.st_mode; ++} ++ ++static int uds_msg_scm_regular_file(int scmfd, int tcpfd, struct uds_event_global_var *p_event_var) ++{ ++ int ret; ++ struct uds_tcp2tcp *p_msg = (struct uds_tcp2tcp *)p_event_var->buf; ++ struct uds_msg_scmrights *p_scmr = (struct uds_msg_scmrights *)&p_msg->data; ++ char *fdproc = calloc(1, UDS_PATH_MAX); ++ if (fdproc == NULL) { ++ uds_err("failed to calloc memory:%lx %lx", fdproc); ++ return EVENT_ERR; ++ } ++ sprintf(fdproc, "/proc/self/fd/%d", scmfd); ++ ret = readlink(fdproc, p_scmr->path, UDS_PATH_MAX); ++ if (ret < 0) { ++ uds_err("readlink:%s error, ret:%d, errstr:%s", fdproc, ret, strerror(errno)); ++ free(fdproc); ++ close(scmfd); ++ return EVENT_ERR; ++ } ++ free(fdproc); ++ p_scmr->flags = fcntl(scmfd, F_GETFL, 0); ++ if (p_scmr->flags < 0) { ++ uds_err("fcntl get flags failed:%d error:%s", p_scmr->flags, strerror(errno)); ++ close(scmfd); ++ return EVENT_ERR; ++ } ++ close(scmfd); ++ p_msg->msgtype = MSG_SCM_RIGHTS; ++ ret = write(tcpfd, p_msg, sizeof(struct uds_tcp2tcp) + sizeof(struct uds_msg_scmrights)); ++ if (ret <= 0) { ++ uds_err("send scm rights msg to tcp failed, ret:%d", ret); ++ return EVENT_ERR; ++ } ++ uds_log("scm rights msg send to tcp, fd:%d path:%s flags:%d", scmfd, p_scmr->path, p_scmr->flags); ++ return EVENT_OK; ++} ++ ++static int uds_msg_scm_fifo_file(int scmfd, int tcpfd, struct uds_event_global_var *p_event_var) ++{ ++#define FDPATH_LEN 32 ++ int ret; ++ struct uds_tcp2tcp *p_get = (struct uds_tcp2tcp *)p_event_var->buf; ++ struct uds_stru_scm_pipe *p_pipe = (struct uds_stru_scm_pipe *)p_get->data; ++ char path[FDPATH_LEN] = {0}; ++ struct stat st; ++ p_get->msgtype = MSG_SCM_PIPE; ++ p_get->msglen = sizeof(struct uds_stru_scm_pipe); ++ ++ sprintf(path, "/proc/self/fd/%d", scmfd); ++ lstat(path, &st); ++ if (st.st_mode & S_IRUSR) { ++ p_pipe->dir = SCM_PIPE_READ; ++ uds_log("scm rights recv read pipe fd:%d, mode:%o", scmfd, st.st_mode); ++ } else if (st.st_mode & S_IWUSR) { ++ p_pipe->dir = SCM_PIPE_WRITE; ++ uds_log("scm rights recv write pipe fd:%d, mode:%o", scmfd, st.st_mode); ++ } else { ++ uds_err("scm rights recv invalid pipe, mode:%o fd:%d", st.st_mode, scmfd); ++ return EVENT_ERR; ++ } ++ p_pipe->srcfd = scmfd; ++ ret = send(tcpfd, p_get, sizeof(struct uds_tcp2tcp) + sizeof(struct uds_stru_scm_pipe), 0); ++ if (ret <= 0) { ++ uds_err("send tar get msg failed, ret:%d errstr:%s", ret, strerror(errno)); ++ return EVENT_ERR; ++ } ++ return EVENT_OK; ++} ++ ++static int uds_msg_scmrights2tcp(struct cmsghdr *cmsg, int tcpfd, struct uds_event_global_var *p_event_var) ++{ ++ int scmfd; ++ mode_t mode; ++ ++ memset(p_event_var->buf, 0, p_event_var->buflen); ++ memcpy(&scmfd, CMSG_DATA(cmsg), sizeof(scmfd)); ++ if (scmfd <= 0) { ++ uds_err("recv invalid scm fd:%d", scmfd); ++ return EVENT_ERR; ++ } ++ ++ mode = uds_msg_file_mode(scmfd); ++ ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ uds_log("recv scmfd:%d from uds, is regular file", scmfd); ++ uds_msg_scm_regular_file(scmfd, tcpfd, p_event_var); ++ break; ++ case S_IFIFO: ++ uds_log("recv scmfd:%d from uds, is fifo", scmfd); ++ uds_msg_scm_fifo_file(scmfd, tcpfd, p_event_var); ++ break; ++ default: ++ uds_err("scm rights not support file mode:%o", mode); ++ break; ++ } ++ ++ return EVENT_OK; ++} ++ ++static int uds_msg_cmsg2tcp(struct msghdr *msg, struct uds_event *evt, struct uds_event_global_var *p_event_var) ++{ ++ int cnt = 0; ++ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); ++ while (cmsg != NULL) { ++ cnt ++; ++ uds_log("cmsg type:%d len:%d level:%d, tcpfd:%d", cmsg->cmsg_type, ++ cmsg->cmsg_len, cmsg->cmsg_level, evt->peer->fd); ++ switch (cmsg->cmsg_type) { ++ case SCM_RIGHTS: ++ uds_msg_scmrights2tcp(cmsg, evt->peer->fd, p_event_var); ++ break; ++ default: ++ uds_err("cmsg type:%d not support now", cmsg->cmsg_type); ++ break; ++ } ++ cmsg = CMSG_NXTHDR(msg, cmsg); ++ } ++ return cnt; ++} ++ ++static int uds_msg_scmfd_combine_msg(struct msghdr *msg, struct cmsghdr **cmsg, int *controllen, int fd) ++{ ++ struct cmsghdr *cnxt = NULL; ++ if (*cmsg == NULL) { ++ cnxt = CMSG_FIRSTHDR(msg); ++ } else { ++ cnxt = CMSG_NXTHDR(msg, *cmsg); ++ } ++ *cmsg = cnxt; ++ cnxt->cmsg_level = SOL_SOCKET; ++ cnxt->cmsg_type = SCM_RIGHTS; ++ cnxt->cmsg_len = CMSG_LEN(sizeof(fd)); ++ memcpy(CMSG_DATA(cnxt), &fd, sizeof(fd)); ++ *controllen = *controllen + cnxt->cmsg_len; ++ return EVENT_OK; ++} ++ ++static int uds_msg_scmright_send_fd(int sock, int fd) ++{ ++ char byte = 0; ++ struct iovec iov; ++ struct msghdr msg; ++ struct cmsghdr *cmsg; ++ char buf[CMSG_SPACE(sizeof(fd))]; ++ ++ // send at least one char ++ memset(&msg, 0, sizeof(msg)); ++ iov.iov_base = &byte; ++ iov.iov_len = 1; ++ msg.msg_iov = &iov; ++ msg.msg_iovlen = 1; ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ ++ ++ msg.msg_control = buf; ++ msg.msg_controllen = sizeof(buf); ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); ++ // Initialize the payload ++ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); ++ msg.msg_controllen = cmsg->cmsg_len; ++ ++ if (sendmsg(sock, &msg, 0) != iov.iov_len) ++ return -1; ++ return 0; ++} ++ ++static int uds_msg_cmsg2uds(struct uds_tcp2tcp *msg, struct uds_event *evt) ++{ ++ int scmfd = -1; ++ switch (msg->msgtype) { ++ case MSG_SCM_RIGHTS: { ++ struct uds_msg_scmrights *p_scmr = (struct uds_msg_scmrights *)&msg->data; ++ int ret; ++ int scmfd = open(p_scmr->path, p_scmr->flags); ++ if (scmfd < 0) { ++ uds_err("scm rights send fd failed, scmfd:%d path:%s flags:%d", scmfd, p_scmr->path, p_scmr->flags); ++ return -1; ++ } ++ uds_log("scm send fd:%d path:%s flags:%d", scmfd, p_scmr->path, p_scmr->flags); ++ break; ++ } ++ default: ++ uds_err("msg type:%d not support.", msg->msgtype); ++ return -1; ++ } ++ return scmfd; ++} ++ ++int uds_msg_tcp2uds_scm_pipe(struct uds_tcp2tcp *p_msg, struct uds_event *evt) ++{ ++ int scmfd; ++ int fd[SCM_PIPE_NUM]; ++ struct uds_stru_scm_pipe *p_pipe = (struct uds_stru_scm_pipe *)p_msg->data; ++ int len = recv(evt->fd, p_pipe, p_msg->msglen, MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("recv data failed, len:%d", len); ++ return EVENT_DEL; ++ } ++ if (p_pipe->dir != SCM_PIPE_READ && p_pipe->dir != SCM_PIPE_WRITE) { ++ uds_err("scm pipe recv invalid pipe dir:%d, srcfd:%d", p_pipe->dir, p_pipe->srcfd); ++ return EVENT_ERR; ++ } ++ struct uds_conn_arg tcp = { ++ .cs = UDS_SOCKET_CLIENT, ++ }; ++ int ret; ++ if ((ret = uds_build_tcp_connection(&tcp)) < 0) { ++ uds_err("build tcp connection failed, return:%d", ret); ++ return EVENT_ERR; ++ } ++ if (pipe(fd) == -1) { ++ uds_err("pipe syscall error, strerr:%s", strerror(errno)); ++ return EVENT_ERR; ++ } ++ if (p_pipe->dir == SCM_PIPE_READ) { ++ uds_log("send read pipe:%d to peer:%d", fd[SCM_PIPE_READ], evt->peer->fd); ++ scmfd = fd[SCM_PIPE_READ]; ++ // read方向,proxy读取消息并转发,此代码处是远端,所以监听tcp换发给pipe write ++ uds_add_pipe_event(tcp.connfd, fd[SCM_PIPE_WRITE], uds_event_tcp2pipe, NULL); ++ } else { ++ uds_log("send write pipe:%d to peer:%d", fd[SCM_PIPE_WRITE], evt->peer->fd); ++ scmfd = fd[SCM_PIPE_WRITE]; ++ // write方向,proxy读取远端代理pipe消息并转发,此处是远端,所以监听pipe read并转发给tcp ++ uds_add_pipe_event(fd[SCM_PIPE_READ], tcp.connfd, uds_event_pipe2tcp, NULL); ++ } ++ ++ p_msg->msgtype = MSGCNTL_PIPE; ++ p_msg->msglen = sizeof(struct uds_stru_scm_pipe); ++ len = write(tcp.connfd, p_msg, sizeof(struct uds_tcp2tcp) + sizeof(struct uds_stru_scm_pipe)); ++ if (len <= 0) { ++ uds_err("send pipe msg failed, len:%d", len); ++ return EVENT_ERR; ++ } ++ uds_log("success to build pipe fd map, dir:%d srcfd:%d tcpfd:%d readfd:%d writefd:%d", ++ p_pipe->dir, p_pipe->srcfd, tcp.connfd, fd[SCM_PIPE_READ], fd[SCM_PIPE_WRITE]); ++ ++ return scmfd; ++} ++ ++int uds_event_tcp2pipe(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ memset(p_event_var->iov_base, 0, p_event_var->iov_len); ++ int len = read(evt->fd, p_event_var->iov_base, p_event_var->iov_len); ++ if (len <= 0) { ++ uds_err("read from tcp failed, len:%d str:%s", len, strerror(errno)); ++ return EVENT_DEL; ++ } ++ ++ uds_log("tcp:%d to pipe:%d len:%d, buf:\n>>>>>>>\n%.*s\n<<<<<<<\n", evt->fd, evt->peerfd, len, len, p_event_var->iov_base); ++ int ret = write(evt->peerfd, p_event_var->iov_base, len); ++ if (ret <= 0) { ++ uds_err("write to pipe failed, fd:%d str:%s", evt->peerfd, strerror(errno)); ++ return EVENT_DEL; ++ } ++ return EVENT_OK; ++} ++ ++int uds_event_pipe2tcp(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ memset(p_event_var->iov_base, 0, p_event_var->iov_len); ++ int len = read(evt->fd, p_event_var->iov_base, p_event_var->iov_len); ++ if (len <= 0) { ++ uds_err("read from pipe failed, len:%d str:%s", len, strerror(errno)); ++ return EVENT_DEL; ++ } ++ ++ uds_log("pipe:%d to tcp:%d len:%d, buf:\n>>>>>>>\n%.*s\n<<<<<<<\n", evt->fd, evt->peerfd, len, len, p_event_var->iov_base); ++ int ret = write(evt->peerfd, p_event_var->iov_base, len); ++ if (ret <= 0) { ++ uds_err("write to tcp failed, fd:%d str:%s", evt->peerfd, strerror(errno)); ++ return EVENT_DEL; ++ } ++ return EVENT_OK; ++ ++} ++ ++int uds_msg_tcp_end_msg(int sock) ++{ ++ struct uds_tcp2tcp end = {.msgtype = MSG_END, .msglen = 0,}; ++ int ret = write(sock, &end, sizeof(struct uds_tcp2tcp)); ++ if (ret <= 0) { ++ uds_err("write end msg failed, ret:%d fd:%d", ret, sock); ++ return EVENT_DEL; ++ } ++ return EVENT_OK; ++} ++ ++void uds_msg_init_event_buf(struct uds_event_global_var *p) ++{ ++ memset(p->iov_base, 0, p->iov_len); ++ memset(p->iov_base_send, 0, p->iov_sendlen); ++ memset(p->msg_control, 0, p->msg_controllen); ++ memset(p->msg_control_send, 0, p->msg_controlsendlen); ++ memset(p->buf, 0, p->buflen); ++ return; ++} ++ ++#define TEST_BUFLEN 256 ++int uds_event_uds2tcp(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ struct uds_event *evt = (struct uds_event *)arg; ++ struct iovec iov; ++ struct msghdr msg; ++ struct cmsghdr *cmsg; ++ int cmsgcnt = 0; ++ int len; ++ ++ memset(&msg, 0, sizeof(msg)); ++ iov.iov_base = p_event_var->iov_base + sizeof(struct uds_tcp2tcp); ++ iov.iov_len = p_event_var->iov_len - sizeof(struct uds_tcp2tcp); ++ msg.msg_iov = &iov; ++ msg.msg_iovlen = 1; ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ ++ msg.msg_control = p_event_var->msg_control; ++ msg.msg_controllen = p_event_var->msg_controllen; ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_len = p_event_var->msg_controllen; ++ ++ len = recvmsg(evt->fd, &msg, 0); ++ if (len == 0) { ++ uds_err("recvmsg error, return:%d", len); ++ uds_event_add_to_free(p_event_var, evt); ++ return EVENT_DEL; ++ } ++ if (len < 0) { ++ uds_err("recvmsg error return val:%d", len); ++ return EVENT_ERR; ++ } ++ cmsg = CMSG_FIRSTHDR(&msg); ++ if (cmsg != NULL) { ++ uds_log("recvmsg cmsg len:%d cmsglen:%d iovlen:%d iov:%s cmsglevel:%d cmsgtype:%d", ++ len, cmsg->cmsg_len, iov.iov_len, iov.iov_base, cmsg->cmsg_level, cmsg->cmsg_type); ++ cmsgcnt = uds_msg_cmsg2tcp(&msg, evt, p_event_var); ++ if (len - cmsgcnt == 0) ++ goto endmsg; ++ } ++ ++ struct uds_tcp2tcp *p_msg = (struct uds_tcp2tcp *)p_event_var->iov_base; ++ p_msg->msgtype = MSG_NORMAL; ++ p_msg->msglen = len; ++ int ret = write(evt->peer->fd, (void *)p_msg, p_msg->msglen + sizeof(struct uds_tcp2tcp)); ++ if (ret <= 0) { ++ uds_err("write to peer:%d failed, retcode:%d len:%d", evt->peer->fd, ret, len); ++ return EVENT_ERR; ++ } ++ ++ uds_log("write iov msg to tcp success, msgtype:%d ret:%d iovlen:%d recvlen:%d udsheadlen:%d msglen:%d msg:\n>>>>>>>\n%.*s\n<<<<<<<\n", ++ p_msg->msgtype, ret, iov.iov_len, len, sizeof(struct uds_tcp2tcp), p_msg->msglen, p_msg->msglen, p_msg->data); ++endmsg: ++ return uds_msg_tcp_end_msg(evt->peer->fd); ++} ++ ++int uds_event_tcp2uds(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++#define MAX_FDS 64 ++ int fds[MAX_FDS] = {0}; ++ int fdnum = 0; ++ struct uds_event *evt = (struct uds_event *)arg; ++ struct uds_tcp2tcp *p_msg = (struct uds_tcp2tcp *)p_event_var->iov_base; ++ int ret; ++ int normal_msg_len = 0; ++ struct msghdr msg; ++ struct cmsghdr *cmsg = NULL; ++ struct iovec iov; ++ int msg_controllen = 0; ++ ++ memset(&msg, 0, sizeof(msg)); ++ iov.iov_base = p_event_var->iov_base_send; ++ iov.iov_len = 0; ++ msg.msg_iov = &iov; ++ msg.msg_iovlen = 1; ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ msg.msg_control = p_event_var->msg_control_send; ++ msg.msg_controllen = p_event_var->msg_controlsendlen; ++ ++ while (1) { ++ int len = recv(evt->fd, p_msg, sizeof(struct uds_tcp2tcp), MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("recv no msg maybe sock is closed, delete this tcp2uds event, len:%d.", len); ++ goto close_event; ++ } ++ uds_log("pmsg:%lx type:%d len:%d iov_base:%lx len:%d", p_msg, p_msg->msgtype, p_msg->msglen, p_event_var->iov_base, len); ++ if (p_msg->msgtype == MSG_END) { ++ break; ++ } ++ if (p_msg->msglen > p_event_var->iov_len - sizeof(struct uds_tcp2tcp) || p_msg->msglen <= 0) { ++ uds_err("pmsg len:%d is invalid, fd:%d peerfd:%d", p_msg->msglen, evt->fd, evt->peer->fd); ++ continue; ++ } ++ switch(p_msg->msgtype) { ++ case MSG_NORMAL: ++ if (normal_msg_len != 0) { ++ uds_err("normal msg repeat recv fd:%d", evt->fd); ++ goto err; ++ } ++ normal_msg_len = recv(evt->fd, p_event_var->iov_base_send, p_msg->msglen, MSG_WAITALL); ++ if (normal_msg_len <= 0) { ++ uds_err("recv msg error:%d fd:%d", len, evt->fd); ++ goto close_event; ++ } ++ iov.iov_len = normal_msg_len; ++ uds_log("recv normal msg len:%d str: \n>>>>>>>\n%.*s\n<<<<<<<", iov.iov_len, iov.iov_len, iov.iov_base); ++ break; ++ case MSG_SCM_RIGHTS: { ++ int len; ++ int scmfd; ++ struct uds_msg_scmrights *p_scm = (struct uds_msg_scmrights *) p_msg->data; ++ memset(p_scm->path, 0, sizeof(p_scm->path)); ++ // SCM RIGHTS msg proc ++ len = recv(evt->fd, p_msg->data, p_msg->msglen, MSG_WAITALL); ++ if (len <= 0) { ++ uds_err("recv data failed len:%d", p_msg->msglen); ++ return EVENT_DEL; ++ } ++ scmfd = uds_msg_cmsg2uds(p_msg, evt); ++ if (scmfd == -1) { ++ goto err; ++ } ++ fds[fdnum++] = scmfd; ++ uds_msg_scmfd_combine_msg(&msg, &cmsg, &msg_controllen, scmfd); ++ break; ++ } ++ case MSG_SCM_PIPE: { ++ int scmfd; ++ scmfd = uds_msg_tcp2uds_scm_pipe(p_msg, evt); ++ if (scmfd == EVENT_DEL) ++ goto close_event; ++ if (scmfd < 0) ++ goto err; ++ fds[fdnum++] = scmfd; ++ uds_msg_scmfd_combine_msg(&msg, &cmsg, &msg_controllen, scmfd); ++ break; ++ } ++ default: ++ uds_err("recv unsupport msg type:%d event fd:%d", p_msg->msgtype, evt->fd); ++ break; ++ } ++ } ++ if (msg_controllen == 0 && iov.iov_len == 0) ++ goto err; ++ msg.msg_controllen = msg_controllen; ++ if (iov.iov_len == 0) iov.iov_len = 1; ++ ret = sendmsg(evt->peer->fd, &msg, 0); ++ uds_log("evt:%d sendmsg len:%d, controllen:%d errno:%s", evt->fd, ret, msg_controllen, strerror(errno)); ++ for (int i = 0; i < fdnum; i++) { ++ close(fds[i]); ++ } ++ return EVENT_OK; ++err: ++ return EVENT_ERR; ++ ++close_event: ++ uds_event_add_to_free(p_event_var, evt); ++ return EVENT_DEL; ++} ++ ++int uds_diag_is_epoll_fd(int fd) ++{ ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) { ++ if (fd == p_uds_var->efd[i]) ++ return 1; ++ } ++ return 0; ++} ++ ++void uds_diag_list_fd(char *buf, int len) ++{ ++#define FDPATH_LEN 32 ++ int pos = 0; ++ char path[32] = {0}; ++ DIR *dir = NULL; ++ struct dirent *entry; ++ dir = opendir("/proc/self/fd/"); ++ if (dir == NULL) { ++ uds_err("open path:/proc/self/fd/ failed"); ++ return; ++ } ++ while (entry = readdir(dir)) { ++ int fd = atoi(entry->d_name); ++ char fdpath[FDPATH_LEN]; ++ char link[FDPATH_LEN]; ++ int ret; ++ if (fd <= 2 || uds_diag_is_epoll_fd(fd)) ++ continue; ++ memset(fdpath, 0, FDPATH_LEN); ++ memset(link, 0, FDPATH_LEN); ++ sprintf(fdpath, "/proc/self/fd/%d", fd); ++ ret = readlink(fdpath, link, FDPATH_LEN); ++ pos += sprintf(&buf[pos], "+ fd:%s type:%u link:%s\n", entry->d_name, entry->d_type, link); ++ } ++ closedir(dir); ++ return; ++} ++ ++int uds_diag_string(char *buf, int len) ++{ ++ int pos = 0; ++ memset(buf, 0, len); ++ pos = sprintf(buf, "+-----------------------------Unix Proxy Diagnostic information-------------------------+\n"); ++ pos += sprintf(&buf[pos], "+ Thread nums:%d\n", p_uds_var->work_thread_num); ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) { ++ pos += sprintf(&buf[pos], "+ Thread %d events count:%d\n", i+1, p_uds_var->work_thread[i].info.events); ++ } ++ pos += sprintf(&buf[pos], "+ Log level:%s\n", p_uds_var->logstr[p_uds_var->loglevel]); ++ strcat(buf, "+---------------------------------------------------------------------------------------+\n"); ++ return strlen(buf); ++} ++ ++// DIAG INFO ++int uds_event_diag_info(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ int connfd; ++ int len; ++ int ret; ++ struct uds_event *evt = (struct uds_event *)arg; ++ if (evt == NULL) { ++ uds_err("param is invalid."); ++ return EVENT_ERR; ++ } ++ connfd = uds_sock_step_accept(evt->fd, AF_UNIX); ++ if (connfd <= 0) { ++ uds_err("conn fd error:%d", connfd); ++ return EVENT_ERR; ++ } ++ ++ uds_log("diag accept an new connection to send diag info, fd:%d", connfd); ++ len = uds_diag_string(p_event_var->iov_base, p_event_var->iov_len); ++ ret = send(connfd, p_event_var->iov_base, len, 0); ++ if (ret <= 0) { ++ uds_err("send diag info error, ret:%d len:%d", ret, len); ++ } ++ close(connfd); ++ return EVENT_OK; ++} ++ ++#define UDS_LOG_STR(level) (level < 0 || level >= UDS_LOG_MAX) ? p_uds_var->logstr[UDS_LOG_MAX] : p_uds_var->logstr[level] ++int uds_event_debug_level(void *arg, int epfd, struct uds_event_global_var *p_event_var) ++{ ++ int connfd; ++ int len; ++ int ret; ++ int cur; ++ struct uds_event *evt = (struct uds_event *)arg; ++ if (evt == NULL) { ++ uds_err("param is invalid."); ++ return EVENT_ERR; ++ } ++ connfd = uds_sock_step_accept(evt->fd, AF_UNIX); ++ if (connfd <= 0) { ++ uds_err("conn fd error:%d", connfd); ++ return EVENT_ERR; ++ } ++ ++ cur = p_uds_var->loglevel; ++ if (cur + 1 < UDS_LOG_MAX) { ++ p_uds_var->loglevel += 1; ++ } else { ++ p_uds_var->loglevel = UDS_LOG_NONE; ++ } ++ ++ uds_log("debug level accept a new connection, current level:%s change to:%s", UDS_LOG_STR(cur), UDS_LOG_STR(p_uds_var->loglevel)); ++ ++ len = sprintf(p_event_var->iov_base, "+---------------UDS LOG LEVEL UPDATE--------------+\n" ++ "+ Log level is:%s before, now change to :%s.\n" ++ "+-------------------------------------------------+\n", UDS_LOG_STR(cur), UDS_LOG_STR(p_uds_var->loglevel)); ++ ++ ret = send(connfd, p_event_var->iov_base, len, 0); ++ if (ret <= 0) { ++ uds_err("send debug level info error, ret:%d len:%d", ret, len); ++ } ++ close(connfd); ++ return EVENT_OK; ++} +diff --git a/qtfs/ipc/uds_event.h b/qtfs/ipc/uds_event.h +new file mode 100644 +index 0000000..a52bf67 +--- /dev/null ++++ b/qtfs/ipc/uds_event.h +@@ -0,0 +1,64 @@ ++#ifndef __QTFS_UDS_EVENT_H__ ++#define __QTFS_UDS_EVENT_H__ ++ ++#define UDS_EVENT_BUFLEN 4096 ++#define UDS_PATH_MAX 1024 ++ ++enum EVENT_RETCODE { ++ EVENT_OK = 0, ++ EVENT_ERR = -1, ++ EVENT_DEL = -2, // del this event after return ++}; ++ ++enum TCP2TCP_TYPE { ++ MSG_NORMAL = 0xa5a5, // 消息类型从特殊数字开始,防止误识别消息 ++ MSG_SCM_RIGHTS, ++ MSG_SCM_CREDENTIALS, // unix domain 扩展消息,预留 ++ MSG_SCM_SECURITY, // unix domain 扩展消息,预留 ++ MSG_GET_TARGET, // 控制消息,用于获取对端的target fd ++ MSG_SCM_PIPE, // 使用SCM传递了一个pipe ++ MSG_END, // tcp消息的结束体 ++}; ++ ++enum TCPCNTL_TYPE { ++ MSGCNTL_UDS = 1, // uds代理模式 ++ MSGCNTL_PIPE, // pipe匿名管道代理模式 ++}; ++ ++// 因为要区分SCM_RIGHTS和普通消息,TCP到TCP需要有一个协议头 ++struct uds_tcp2tcp { ++ int msgtype; ++ int msglen; // len of data ++ char data[0]; ++}; ++ ++struct uds_msg_scmrights { ++ int flags; // open flags ++ char path[UDS_PATH_MAX]; ++}; ++ ++enum { ++ SCM_PIPE_READ = 0, ++ SCM_PIPE_WRITE, ++ SCM_PIPE_NUM, ++}; ++ ++struct uds_stru_scm_pipe { ++ int dir; // 0: send read filedes; 1: send write filedes ++ // proxy通过scm rights接收到员pipe fd,后面消息回来时事件 ++ // 会发生变化,所以需要回消息时带上,才能建立关联 ++ int srcfd; ++}; ++ ++int uds_event_uds_listener(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_tcp_listener(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_diag_info(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_debug_level(void *arg, int epfd, struct uds_event_global_var *p_event_var); ++int uds_event_pre_handler(struct uds_event *evt); ++int uds_event_pre_hook(struct uds_event_global_var *p_event_var); ++int uds_event_post_hook(struct uds_event_global_var *p_event_var); ++int uds_event_module_init(struct uds_event_global_var *p_event_var); ++void uds_event_module_fini(struct uds_event_global_var *p); ++ ++#endif ++ +diff --git a/qtfs/ipc/uds_main.c b/qtfs/ipc/uds_main.c +new file mode 100644 +index 0000000..b479a60 +--- /dev/null ++++ b/qtfs/ipc/uds_main.c +@@ -0,0 +1,556 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../comm.h" ++#include "uds_main.h" ++#include "uds_event.h" ++ ++struct uds_global_var g_uds_var = {.logstr = {"NONE", "ERROR", "INFO", "UNKNOWN"}}; ++struct uds_global_var *p_uds_var = &g_uds_var; ++struct uds_event_global_var *g_event_var = NULL; ++ ++struct uds_event *uds_alloc_event() ++{ ++ struct uds_event *p = (struct uds_event *)malloc(sizeof(struct uds_event)); ++ if (p == NULL) { ++ uds_err("malloc failed."); ++ return NULL; ++ } ++ memset(p, 0, sizeof(struct uds_event)); ++ return p; ++} ++ ++int uds_event_insert(int efd, struct uds_event *event) ++{ ++ struct epoll_event evt; ++ evt.data.ptr = (void *)event; ++ evt.events = EPOLLIN; ++ if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, event->fd, &evt)) { ++ uds_err("epoll ctl add fd:%d event failed.", event->fd); ++ return -1; ++ } ++ return 0; ++} ++ ++int uds_event_suspend(int efd, struct uds_event *event) ++{ ++ int ret = epoll_ctl(efd, EPOLL_CTL_DEL, event->fd, NULL); ++ if (ret != 0) { ++ uds_err("failed to suspend fd:%d.", event->fd); ++ return -1; ++ } ++ return 0; ++} ++ ++int uds_event_delete(int efd, int fd) ++{ ++ int ret = epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL); ++ if (ret != 0) { ++ uds_err("failed to delete event fd:%d.", fd); ++ } else { ++ uds_log("event fd:%d deleted.", fd); ++ } ++ close(fd); ++ return ret; ++} ++ ++void uds_main_loop(int efd, struct uds_thread_arg *arg) ++{ ++ int n = 0; ++ int ret; ++ struct uds_event *udsevt; ++ struct epoll_event *evts = NULL; ++ struct uds_event_global_var *p_event_var = arg->p_event_var; ++ if (p_event_var == NULL) { ++ uds_err("event variable invalid."); ++ return; ++ } ++ ++ evts = calloc(UDS_EPOLL_MAX_EVENTS, sizeof(struct epoll_event)); ++ if (evts == NULL) { ++ uds_err("init calloc evts failed."); ++ return; ++ } ++ if (uds_event_module_init(p_event_var) == EVENT_ERR) { ++ uds_err("uds event module init failed, main loop not run."); ++ return; ++ } ++#ifdef QTFS_SERVER ++ extern int engine_run; ++ while (engine_run) { ++#else ++ while (1) { ++#endif ++ n = epoll_wait(efd, evts, UDS_EPOLL_MAX_EVENTS, 1000); ++ if (n == 0) ++ continue; ++ if (n < 0) { ++ uds_err("epoll wait return errcode:%d", n); ++ continue; ++ } ++ arg->info.events += n; ++ uds_event_pre_hook(p_event_var); ++ for (int i = 0; i < n; i++) { ++ udsevt = (struct uds_event *)evts[i].data.ptr; ++ uds_log("event fd:%d events:%d tofree:%d", udsevt->fd, evts[i].events, udsevt->tofree); ++ if (udsevt->handler == NULL) { ++ uds_err("bad event, fd:%d handler is NULL.", udsevt->fd); ++ continue; ++ } ++ // 预检查失败择不执行handler ++ if (uds_event_pre_handler(udsevt) == EVENT_ERR) { ++ continue; ++ } ++ ret = udsevt->handler(udsevt, efd, p_event_var); ++ // 此处释放当前事件,peer事件需要handler里面释放 ++ if (ret == EVENT_DEL) { ++ uds_del_event(udsevt); ++ } ++ } ++ uds_event_post_hook(p_event_var); ++ } ++ uds_log("main loop exit."); ++ uds_event_module_fini(p_event_var); ++ return; ++} ++ ++int uds_build_tcp_connection(struct uds_conn_arg *arg) ++{ ++ const int sock_max_conn_num = 1024; ++ ++ if (arg->cs > UDS_SOCKET_SERVER) { ++ uds_err("cs type %d is error.", arg->cs); ++ return -1; ++ } ++ struct sockaddr_in sock_addr = { ++ .sin_family = AF_INET, ++ }; ++ int sock_fd = socket(AF_INET, SOCK_STREAM, 0); ++ ++ if (sock_fd < 0) { ++ uds_err("As %s failed, socket fd: %d, err:%s.", ++ (arg->cs == UDS_SOCKET_CLIENT) ? "client" : "server", ++ sock_fd, strerror(errno)); ++ return -1; ++ } ++ arg->sockfd = sock_fd; ++ ++ if (arg->cs == UDS_SOCKET_SERVER) { ++ sock_addr.sin_port = htons(p_uds_var->tcp.port); ++ sock_addr.sin_addr.s_addr = inet_addr(p_uds_var->tcp.addr); ++ if (bind(sock_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { ++ uds_err("As server failed, bind error, err:%s.", ++ strerror(errno)); ++ goto close_and_return; ++ } ++ if (listen(sock_fd, sock_max_conn_num) < 0) { ++ uds_err("As server listen failed, err:%s.", strerror(errno)); ++ goto close_and_return; ++ } ++ } else { ++ sock_addr.sin_port = htons(p_uds_var->tcp.peerport); ++ sock_addr.sin_addr.s_addr = inet_addr(p_uds_var->tcp.peeraddr); ++ if (connect(arg->sockfd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_in)) < 0) { ++ goto close_and_return; ++ } ++ arg->connfd = sock_fd; ++ uds_log("Connect to server successed, ip:%s port:%u", p_uds_var->tcp.peeraddr, p_uds_var->tcp.peerport); ++ } ++ ++ return 0; ++close_and_return: ++ close(sock_fd); ++ return -1; ++} ++ ++int uds_build_unix_connection(struct uds_conn_arg *arg) ++{ ++ const int sock_max_conn_num = 5; ++ if (arg->cs > UDS_SOCKET_SERVER) { ++ uds_err("cs type %d is error.", arg->cs); ++ return -1; ++ } ++ struct sockaddr_un sock_addr = { ++ .sun_family = AF_UNIX, ++ }; ++ int sock_fd = socket(AF_UNIX, arg->udstype, 0); ++ ++ if (sock_fd < 0) { ++ uds_err("As %s failed, socket fd: %d, err:%s.", ++ (arg->cs == UDS_SOCKET_CLIENT) ? "client" : "server", ++ sock_fd, strerror(errno)); ++ return -1; ++ } ++ strncpy(sock_addr.sun_path, arg->sun_path, sizeof(sock_addr.sun_path)); ++ arg->sockfd = sock_fd; ++ ++ if (arg->cs == UDS_SOCKET_SERVER) { ++ unlink(sock_addr.sun_path); ++ if (bind(sock_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { ++ uds_err("As server failed, bind error, err:%s.", ++ strerror(errno)); ++ goto close_and_return; ++ } ++ if (listen(sock_fd, sock_max_conn_num) < 0) { ++ uds_err("As server listen failed, err:%s.", strerror(errno)); ++ goto close_and_return; ++ } ++ } else { ++ if (connect(arg->sockfd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_un)) < 0) { ++ goto close_and_return; ++ } ++ arg->connfd = sock_fd; ++ uds_log("Connect to server successed, sun path:%s", arg->sun_path); ++ } ++ ++ return 0; ++close_and_return: ++ uds_log("close sockfd:%d and return", sock_fd); ++ close(sock_fd); ++ return -1; ++ ++} ++ ++int uds_sock_step_accept(int sock_fd, int family) ++{ ++ struct sockaddr_in in_addr; ++ struct sockaddr_un un_addr; ++ socklen_t len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_un); ++ int connfd; ++ if (family == AF_INET) { ++ connfd = accept(sock_fd, (struct sockaddr *)&in_addr, &len); ++ } else { ++ connfd = accept(sock_fd, (struct sockaddr *)&un_addr, &len); ++ } ++ if (connfd < 0) { ++ uds_err("Accept error:%d, err:%s.", connfd, strerror(errno)); ++ return connfd; ++ } ++ if (family == AF_INET) { ++ uds_log("Accept success, ip:%s, port:%u", ++ inet_ntoa(in_addr.sin_addr), ++ ntohs(in_addr.sin_port)); ++ } else { ++ uds_log("Accept success, sun path:%s", un_addr.sun_path); ++ } ++ return connfd; ++} ++ ++struct uds_event *uds_add_event(int fd, struct uds_event *peer, int (*handler)(void *, int, struct uds_event_global_var *), void *priv) ++{ ++ struct uds_event *newevt = uds_alloc_event(); ++ int hash = fd % p_uds_var->work_thread_num; ++ if (newevt == NULL || p_uds_var->efd[hash] <= 0) { ++ uds_err("alloc event failed, efd:%d hash:%d", p_uds_var->efd[hash], hash); ++ return NULL; ++ } ++ ++ newevt->fd = fd; ++ newevt->peer = peer; // 如果tcp回应,消息转回uds这个fd ++ newevt->handler = handler; ++ newevt->priv = priv; ++ newevt->tofree = 0; ++ uds_event_insert(p_uds_var->efd[hash], newevt); ++ return newevt; ++} ++ ++struct uds_event *uds_add_pipe_event(int fd, int peerfd, int (*handler)(void *, int, struct uds_event_global_var *), void *priv) ++{ ++ int hash = fd % p_uds_var->work_thread_num; ++ struct uds_event *newevt = uds_alloc_event(); ++ if (newevt == NULL || p_uds_var->efd[hash] <= 0) { ++ uds_err("alloc event failed, efd:%d", p_uds_var->efd[hash]); ++ return NULL; ++ } ++ ++ newevt->fd = fd; ++ newevt->peerfd = peerfd; // 如果tcp回应,消息转回uds这个fd ++ newevt->handler = handler; ++ newevt->priv = priv; ++ newevt->tofree = 0; ++ newevt->pipe = 1; ++ uds_event_insert(p_uds_var->efd[hash], newevt); ++ return newevt; ++} ++ ++void uds_del_event(struct uds_event *evt) ++{ ++ int hash = evt->fd % p_uds_var->work_thread_num; ++ if (evt->pipe == 1 &&evt->peerfd != -1) { ++ // pipe是单向,peerfd没有epoll事件,所以直接关闭 ++ close(evt->peerfd); ++ evt->peerfd = -1; ++ } ++ uds_event_delete(p_uds_var->efd[hash], evt->fd); ++ free(evt); ++ return; ++} ++ ++void uds_thread_diag_init(struct uds_thread_info *info) ++{ ++ info->events = 0; ++ info->fdnum = 0; ++} ++ ++void *uds_proxy_thread(void *arg) ++{ ++ struct uds_thread_arg *parg = (struct uds_thread_arg *)arg; ++ uds_thread_diag_init(&parg->info); ++ uds_main_loop(parg->efd, parg); ++ return NULL; ++} ++ ++struct uds_event *uds_init_unix_listener(const char *addr, int (*handler)(void *, int, struct uds_event_global_var *)) ++{ ++ struct uds_event *udsevt; ++ struct uds_conn_arg arg; ++ struct uds_conn_arg *parg = &arg; ++ ++ parg->cs = UDS_SOCKET_SERVER; ++ strncpy(parg->sun_path, addr, sizeof(parg->sun_path)); ++ parg->udstype = SOCK_STREAM; ++ if (uds_build_unix_connection(parg) != 0) ++ return NULL; ++ udsevt = uds_add_event(parg->sockfd, NULL, handler, NULL); ++ if (udsevt == NULL) { ++ uds_err("add unix listener event failed."); ++ return NULL; ++ } ++ return udsevt; ++} ++ ++struct uds_event *uds_init_tcp_listener() ++{ ++ struct uds_event *tcpevt; ++ struct uds_conn_arg arg; ++ struct uds_conn_arg *parg = &arg; ++ parg->cs = UDS_SOCKET_SERVER; ++ if (uds_build_tcp_connection(parg) != 0) ++ return NULL; ++ ++ tcpevt = uds_add_event(parg->sockfd, NULL, uds_event_tcp_listener, NULL); ++ if (tcpevt == NULL) ++ return NULL; ++ return tcpevt; ++} ++ ++void uds_thread_create() ++{ ++ struct uds_conn_arg arg; ++ struct uds_conn_arg *parg = &arg; ++ struct uds_event *udsevt; ++ struct uds_event *tcpevt; ++ struct uds_event *diagevt; ++ struct uds_event *logevt; ++ int efd; ++ ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) { ++ efd = epoll_create1(0); ++ if (efd == -1) { ++ uds_err("epoll create1 failed, i:%d.", i); ++ return; ++ } ++ p_uds_var->efd[i] = efd; ++ } ++ ++ if ((udsevt = uds_init_unix_listener(UDS_BUILD_CONN_ADDR, uds_event_uds_listener)) == NULL) ++ return; ++ ++ if ((tcpevt = uds_init_tcp_listener()) == NULL) ++ goto end; ++ ++ if ((diagevt = uds_init_unix_listener(UDS_DIAG_ADDR, uds_event_diag_info)) == NULL) ++ goto end1; ++ ++ if ((logevt = uds_init_unix_listener(UDS_LOGLEVEL_UPD, uds_event_debug_level)) == NULL) ++ goto end2; ++ ++ do { ++ pthread_t *thrd = (pthread_t *)malloc(sizeof(pthread_t) * p_uds_var->work_thread_num); ++ struct uds_thread_arg *work_thread; ++ if (thrd == NULL) { ++ uds_err("thread info malloc failed."); ++ break; ++ } ++ work_thread = (struct uds_thread_arg *)malloc(sizeof(struct uds_thread_arg *) * p_uds_var->work_thread_num); ++ if (work_thread == NULL) { ++ uds_err("thread arg malloc failed."); ++ free(thrd); ++ break; ++ } ++ ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) { ++ p_uds_var->work_thread[i].p_event_var = &g_event_var[i]; ++ p_uds_var->work_thread[i].efd = p_uds_var->efd[i]; ++ (void)pthread_create(&thrd[i], NULL, uds_proxy_thread, &p_uds_var->work_thread[i]); ++ } ++ p_uds_var->loglevel = UDS_LOG_NONE; ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) ++ pthread_join(thrd[i], NULL); ++ free(thrd); ++ free(work_thread); ++ } while(0); ++end2: ++ uds_del_event(diagevt); ++end1: ++ uds_del_event(tcpevt); ++end: ++ uds_del_event(udsevt); ++ for (int i = 0; i < p_uds_var->work_thread_num; i++) ++ close(p_uds_var->efd[i]); ++ ++ return; ++} ++ ++int uds_set_pid() ++{ ++ int fd = -1; ++ if (access(QTFS_CLIENT_DEV, 0) == 0) { ++ fd = open(QTFS_CLIENT_DEV, O_RDONLY | O_NONBLOCK); ++ if (fd < 0) ++ goto open_failed; ++ goto set; ++ } ++ if (access(QTFS_SERVER_DEV, 0) == 0) { ++ fd = open(QTFS_SERVER_DEV, O_RDONLY | O_NONBLOCK); ++ if (fd < 0) ++ goto open_failed; ++ goto set; ++ } ++ uds_err("qtfs dev(<%s> or <%s>) both not exist", QTFS_CLIENT_DEV, QTFS_SERVER_DEV); ++ return EVENT_ERR; ++ ++open_failed: ++ uds_err("open %s failed, ret:%d", QTFS_CLIENT_DEV, fd); ++ return EVENT_ERR; ++ ++set: ++ do { ++ int pid = getpid(); ++ int ret = ioctl(fd, QTFS_IOCTL_UDS_PROXY_PID, &pid); ++ if (ret < 0) { ++ uds_err("ioctl failed to set pid:%d ret:%d", pid, ret); ++ return EVENT_ERR; ++ } ++ uds_log("set proxy pid:%d to qtfs successed.", pid); ++ } while (0); ++ close(fd); ++ return EVENT_OK; ++} ++ ++int uds_env_prepare() ++{ ++ DIR *dir; ++ if (access(UDS_BUILD_CONN_ADDR, 0) == 0) ++ return EVENT_OK; ++ ++ if ((dir = opendir(UDS_BUILD_CONN_DIR)) == NULL) { ++ if (mkdir(UDS_BUILD_CONN_DIR, 0755) < 0) { ++ uds_err("mkdir %s failed.", UDS_BUILD_CONN_DIR); ++ } ++ } else { ++ closedir(dir); ++ } ++ int fd = open(UDS_BUILD_CONN_ADDR, O_RDONLY|O_CREAT, 0700); ++ if (fd < 0) { ++ uds_err("create file:%s failed.", UDS_BUILD_CONN_ADDR); ++ return EVENT_ERR; ++ } ++ uds_log("success to create %s.", UDS_BUILD_CONN_ADDR); ++ close(fd); ++ return EVENT_OK; ++} ++ ++static void uds_sig_pipe(int signum) ++{ ++ uds_log("uds proxy recv sigpipe and ignore"); ++} ++ ++void uds_helpinfo(char *argv[]) ++{ ++ uds_err("Usage:"); ++ uds_err(" %s .", argv[0]); ++ uds_err("Param:"); ++ uds_err(" - server ip address"); ++ uds_err(" - port number"); ++ uds_err(" - peer address"); ++ uds_err(" - peer port"); ++ return; ++} ++ ++/* ++ * uds跨主机协同主程序,设计成镜像的,每一端2个线程:send thread、recv thread ++ * 在server侧线程由原engine拉起,在client侧新起一个engine进程 ++ */ ++#ifdef QTFS_SERVER ++int uds_proxy_main(int argc, char *argv[]) ++#else ++int main(int argc, char *argv[]) ++#endif ++{ ++ p_uds_var->loglevel = UDS_LOG_INFO; ++#define ARG_NUM 6 ++ if (argc != ARG_NUM) { ++ uds_helpinfo(argv); ++ return -1; ++ } ++ if (uds_set_pid() != EVENT_OK) { ++ uds_err("proxy failed to set pid."); ++ return -1; ++ } ++ if (uds_env_prepare() != EVENT_OK) { ++ uds_err("proxy prepare environment failed."); ++ return -1; ++ } ++ signal(SIGPIPE, uds_sig_pipe); ++ p_uds_var->work_thread_num = atoi(argv[1]); ++ if (p_uds_var->work_thread_num <= 0 || p_uds_var->work_thread_num > UDS_WORK_THREAD_MAX) { ++ uds_err("work thread num:%d is too large.(must small or equal than %d)", p_uds_var->work_thread_num, UDS_WORK_THREAD_MAX); ++ return -1; ++ } ++ p_uds_var->efd = (int *)malloc(sizeof(int) * p_uds_var->work_thread_num); ++ if (p_uds_var->efd == NULL) { ++ uds_err("efd malloc failed, num:%d", p_uds_var->work_thread_num); ++ return -1; ++ } ++ ++ p_uds_var->work_thread = (struct uds_thread_arg *)malloc(sizeof(struct uds_thread_arg) * p_uds_var->work_thread_num); ++ if (p_uds_var->work_thread == NULL) { ++ uds_err("work thread var malloc failed."); ++ return -1; ++ } ++ p_uds_var->tcp.port = atoi(argv[3]); ++ strncpy(p_uds_var->tcp.addr, argv[2], 20); ++ p_uds_var->tcp.peerport = atoi(argv[5]); ++ strncpy(p_uds_var->tcp.peeraddr, argv[4], 20); ++ ++ uds_log("uds proxy param thread num:%d ip:%s port:%u peerip:%s port:%u", ++ p_uds_var->work_thread_num, p_uds_var->tcp.addr, p_uds_var->tcp.port, ++ p_uds_var->tcp.peeraddr, p_uds_var->tcp.peerport); ++ g_event_var = (struct uds_event_global_var *)malloc(sizeof(struct uds_event_global_var) * p_uds_var->work_thread_num); ++ if (g_event_var == NULL) { ++ uds_err("event variable malloc failed"); ++ return -1; ++ } ++ uds_thread_create(); ++ ++ return 0; ++} +diff --git a/qtfs/ipc/uds_main.h b/qtfs/ipc/uds_main.h +new file mode 100644 +index 0000000..793cd2f +--- /dev/null ++++ b/qtfs/ipc/uds_main.h +@@ -0,0 +1,141 @@ ++#ifndef __QTFS_UDS_MAIN_H__ ++#define __QTFS_UDS_MAIN_H__ ++ ++#include ++ ++#include "uds_module.h" ++ ++#define UDS_EPOLL_MAX_EVENTS 64 ++#define UDS_WORK_THREAD_MAX 64 ++ ++extern struct uds_global_var *p_uds_var; ++ ++enum { ++ UDS_LOG_NONE, ++ UDS_LOG_ERROR, ++ UDS_LOG_INFO, ++ UDS_LOG_MAX, ++}; ++ ++#define uds_log(info, ...) \ ++ if (p_uds_var->loglevel >= UDS_LOG_INFO) {\ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } ++ ++#define uds_log2(info, ...) \ ++ if (p_uds_var->loglevel >= UDS_LOG_INFO) {\ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } ++ ++#define uds_err(info, ...) \ ++ if (p_uds_var->loglevel >= UDS_LOG_ERROR) {\ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][ERROR:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } ++ ++enum { ++ UDS_THREAD_EPWAIT = 1, // epoll wait status ++}; ++struct uds_thread_info { ++ int fdnum; ++ ++ int events; ++ int status; ++}; ++ ++struct uds_event_global_var { ++ int cur; ++ struct uds_event *tofree[UDS_EPOLL_MAX_EVENTS]; ++ char *msg_control; ++ int msg_controllen; ++ char *msg_control_send; ++ int msg_controlsendlen; ++ char *iov_base; ++ int iov_len; ++ char *iov_base_send; ++ int iov_sendlen; ++ char *buf; ++ int buflen; ++}; ++ ++struct uds_event { ++ int fd; /* 本事件由这个fd触发 */ ++ unsigned int tofree : 1, /* 1--in to free list; 0--not */ ++ pipe : 1, // this is a pipe event ++ reserved : 30; ++ union { ++ struct uds_event *peer; /* peer event */ ++ int peerfd; // scm pipe 场景单向导通,只需要一个fd即可 ++ }; ++ int (*handler)(void *, int, struct uds_event_global_var *); /* event处理函数 */ ++ void *priv; // private data ++ char cpath[UDS_SUN_PATH_LEN]; ++ char spath[UDS_SUN_PATH_LEN]; ++}; ++ ++ ++struct uds_thread_arg { ++ int efd; ++ struct uds_event_global_var *p_event_var; ++ struct uds_thread_info info; ++}; ++ ++struct uds_global_var { ++ int work_thread_num; ++ int *efd; ++ struct uds_thread_arg *work_thread; ++ int loglevel; ++ char *logstr[UDS_LOG_MAX + 1]; ++ struct _tcp { ++ char addr[20]; ++ unsigned short port; ++ char peeraddr[20]; ++ unsigned short peerport; ++ } tcp; ++ struct _uds { ++ char sun_path[UDS_SUN_PATH_LEN]; ++ } uds; ++}; ++enum uds_cs { ++ UDS_SOCKET_CLIENT = 1, ++ UDS_SOCKET_SERVER, ++}; ++ ++struct uds_conn_arg { ++ int cs; // client(1) or server(2) ++ ++ int udstype; // DGRAM or STREAM ++ char sun_path[UDS_SUN_PATH_LEN]; ++ int sockfd; ++ int connfd; ++}; ++ ++struct uds_event *uds_add_event(int fd, struct uds_event *peer, int (*handler)(void *, int, struct uds_event_global_var *), void *priv); ++struct uds_event *uds_add_pipe_event(int fd, int peerfd, int (*handler)(void *, int, struct uds_event_global_var *), void *priv); ++int uds_sock_step_accept(int sockFd, int family); ++int uds_build_tcp_connection(struct uds_conn_arg *arg); ++int uds_build_unix_connection(struct uds_conn_arg *arg); ++void uds_del_event(struct uds_event *evt); ++int uds_event_suspend(int efd, struct uds_event *event); ++int uds_event_insert(int efd, struct uds_event *event); ++#ifdef QTFS_SERVER ++int uds_proxy_main(int argc, char *argv[]); ++#endif ++#endif +diff --git a/qtfs/ipc/uds_module.h b/qtfs/ipc/uds_module.h +new file mode 100644 +index 0000000..9ccbb9d +--- /dev/null ++++ b/qtfs/ipc/uds_module.h +@@ -0,0 +1,19 @@ ++#ifndef __QTFS_UDS_MODULE_H__ ++#define __QTFS_UDS_MODULE_H__ ++ ++#define UDS_BUILD_CONN_ADDR "/var/run/qtfs/remote_uds.sock" ++#define UDS_DIAG_ADDR "/var/run/qtfs/uds_proxy_diag.sock" ++#define UDS_LOGLEVEL_UPD "/var/run/qtfs/uds_loglevel.sock" ++#define UDS_BUILD_CONN_DIR "/var/run/qtfs/" ++ ++#define UDS_SUN_PATH_LEN 108 // from glibc ++struct uds_proxy_remote_conn_req { ++ unsigned short type; ++ unsigned short resv; ++ char sun_path[UDS_SUN_PATH_LEN]; ++}; ++struct uds_proxy_remote_conn_rsp { ++ int ret; ++}; ++ ++#endif +diff --git a/qtfs/misc.c b/qtfs/misc.c +index 98222bd..44da4e1 100644 +--- a/qtfs/misc.c ++++ b/qtfs/misc.c +@@ -156,6 +156,13 @@ long qtfs_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + } + break; + } ++ case QTFS_IOCTL_UDS_PROXY_PID: ++ if (copy_from_user(&qtfs_uds_proxy_pid, (void *)arg, sizeof(int))) { ++ qtfs_err("ioctl get uds proxy pid failed."); ++ break; ++ } ++ qtfs_info("ioctl get uds proxy process pid is %d", qtfs_uds_proxy_pid); ++ break; + } + return ret; + } +diff --git a/qtfs/qtfs/sb.c b/qtfs/qtfs/sb.c +index 7445fad..104d137 100644 +--- a/qtfs/qtfs/sb.c ++++ b/qtfs/qtfs/sb.c +@@ -288,7 +288,7 @@ ssize_t qtfs_readiter(struct kiocb *kio, struct iov_iter *iov) + + req->fd = private->fd; + if (req->fd <= 0) { +- qtfs_err("qtfs_readiter: invalid file(0x%llx)", req->fd); ++ qtfs_err("qtfs_readiter: invalid file(%d)", req->fd); + qtfs_conn_put_param(pvar); + return -EINVAL; + } +@@ -360,7 +360,7 @@ ssize_t qtfs_writeiter(struct kiocb *kio, struct iov_iter *iov) + + req->d.fd = private->fd; + if (req->d.fd < 0) { +- qtfs_err("qtfs_write: invalid file(0x%llx)", req->d.fd); ++ qtfs_err("qtfs_write: invalid file(%d)", req->d.fd); + qtfs_conn_put_param(pvar); + return -EINVAL; + } +@@ -1172,7 +1172,7 @@ int qtfs_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsi + *stat = rsp->stat; + 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 || rsp->stat.mode != inode->i_mode) { ++ if (inode->i_ino != rsp->stat.ino || inode->i_mode != rsp->stat.mode) { + if (inode->i_nlink > 0){ + drop_nlink(inode); + } +diff --git a/qtfs/qtfs_server/Makefile b/qtfs/qtfs_server/Makefile +index 9c6bcd5..2ff826f 100644 +--- a/qtfs/qtfs_server/Makefile ++++ b/qtfs/qtfs_server/Makefile +@@ -4,15 +4,26 @@ KBUILD=/lib/modules/$(shell uname -r)/build/ + obj-m:=qtfs_server.o + qtfs_server-objs:=../conn.o fsops.o qtfs-server.o ../misc.o + ++DEPGLIB=-lglib-2.0 -I../ -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include ++ + all: qtfs_server engine + + qtfs_server: + make -C $(KBUILD) M=$(PWD) modules + +-engine: +- gcc -O2 -o engine user_engine.c -lpthread -lglib-2.0 -I../ -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -DQTFS_SERVER ++engine: uds_event.o uds_main.o user_engine.o ++ gcc -O2 -o engine $^ -lpthread $(DEPGLIB) -I../ -I../ipc/ -DQTFS_SERVER ++ ++user_engine.o: ++ cc -g -c -o user_engine.o user_engine.c $(DEPGLIB) -I../ -DQTFS_SERVER ++ ++uds_event.o: ++ cc -g -c -o uds_event.o ../ipc/uds_event.c -DQTFS_SERVER ++ ++uds_main.o: ++ cc -g -c -o uds_main.o ../ipc/uds_main.c -DQTFS_SERVER + + clean: + make -C $(KBUILD) M=$(PWD) clean + rm -rf engine +- rm -rf ../*.o ../.*.o.cmd ++ rm -rf ../*.o +diff --git a/qtfs/qtfs_server/fsops.c b/qtfs/qtfs_server/fsops.c +index 61e8895..6c3e201 100644 +--- a/qtfs/qtfs_server/fsops.c ++++ b/qtfs/qtfs_server/fsops.c +@@ -25,10 +25,11 @@ + + bool in_white_list(char *path, int type) + { ++ int i, in_wl = -1; ++ + if (!whitelist[type]) { + return true; + } +- int i, in_wl = -1; + for (i = 0; i < whitelist[type]->len; i++) { + if (!strncmp(path, whitelist[type]->wl[i].path, whitelist[type]->wl[i].len)){ + in_wl = i; +@@ -202,7 +203,7 @@ static int handle_statfs(struct qtserver_arg *arg) + static int handle_mount(struct qtserver_arg *arg) + { + struct path path; +- int ret, i, in_wl = -1; ++ 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)) { +diff --git a/qtfs/qtfs_server/qtfs-server.c b/qtfs/qtfs_server/qtfs-server.c +index b0b8ab0..cbe07f0 100644 +--- a/qtfs/qtfs_server/qtfs-server.c ++++ b/qtfs/qtfs_server/qtfs-server.c +@@ -214,11 +214,6 @@ long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long a + qtfs_server_thread_run = arg; + break; + +- case QTFS_IOCTL_ALLINFO: +- case QTFS_IOCTL_CLEARALL: +- case QTFS_IOCTL_LOGLEVEL: +- ret = qtfs_misc_ioctl(file, cmd, arg); +- break; + case QTFS_IOCTL_WHITELIST: + if (copy_from_user(&len, (void __user *)arg, sizeof(int))) { + qtfs_err("qtfs ioctl white init copy from user failed."); +@@ -239,6 +234,12 @@ long qtfs_server_misc_ioctl(struct file *file, unsigned int cmd, unsigned long a + qtfs_err("init %d list:%d %s", tmp->type, i, whitelist[tmp->type]->wl[i].path); + } + break; ++ case QTFS_IOCTL_ALLINFO: ++ case QTFS_IOCTL_CLEARALL: ++ case QTFS_IOCTL_LOGLEVEL: ++ case QTFS_IOCTL_UDS_PROXY_PID: ++ ret = qtfs_misc_ioctl(file, cmd, arg); ++ break; + default: + qtfs_err("qtfs misc ioctl unknown cmd:%u.", cmd); + break; +diff --git a/qtfs/qtfs_server/user_engine.c b/qtfs/qtfs_server/user_engine.c +index 547935c..a3d627d 100644 +--- a/qtfs/qtfs_server/user_engine.c ++++ b/qtfs/qtfs_server/user_engine.c +@@ -14,6 +14,7 @@ + #include + + #include "comm.h" ++#include "ipc/uds_main.h" + + char wl_type_str[QTFS_WHITELIST_MAX][10] = {"Open", "Write", "Read", "Readdir", "Mkdir", "Rmdir", "Create", "Unlink", "Rename", "Setattr", "Setxattr", "Mount"}; + +@@ -220,13 +221,12 @@ int qtfs_whitelist_init(int fd) + + int main(int argc, char *argv[]) + { +- if (argc != 3) { +- engine_out("Usage: %s .", argv[0]); +- engine_out(" Example: %s 4096 16.", argv[0]); ++ if (argc != 7) { ++ engine_out("Usage: %s .", argv[0]); ++ engine_out(" Example: %s 16 1 192.168.10.10 12121 192.168.10.11 12121.", argv[0]); + return -1; + } +- int psize = atoi(argv[1]); +- int thread_nums = atoi(argv[2]); ++ int thread_nums = atoi(argv[1]); + int fd = open(QTFS_SERVER_FILE, O_RDONLY); + if (fd < 0) { + engine_err("qtfs server file:%s open failed, fd:%d.", QTFS_SERVER_FILE, fd); +@@ -247,9 +247,9 @@ int main(int argc, char *argv[]) + + pthread_t texec[QTFS_MAX_THREADS]; + pthread_t tepoll; +- if (psize > QTFS_USERP_MAXSIZE || thread_nums > QTFS_MAX_THREADS) { +- engine_err("qtfs engine param invalid, size:%d(must <= %d) thread_nums:%d(must <= %d).", +- psize, QTFS_USERP_MAXSIZE, thread_nums, QTFS_MAX_THREADS); ++ if (thread_nums > QTFS_MAX_THREADS) { ++ engine_err("qtfs engine param invalid, thread_nums:%d(must <= %d).", ++ thread_nums, QTFS_MAX_THREADS); + goto end; + } + (void)ioctl(fd, QTFS_IOCTL_EXIT, 1); +@@ -257,24 +257,30 @@ int main(int argc, char *argv[]) + signal(SIGKILL, qtfs_signal_int); + signal(SIGTERM, qtfs_signal_int); + +- struct qtfs_server_userp_s *userp = qtfs_engine_thread_init(fd, thread_nums, psize); ++ struct qtfs_server_userp_s *userp = qtfs_engine_thread_init(fd, thread_nums, QTFS_USERP_SIZE); + if (userp == NULL) { + engine_out("qtfs engine userp init failed."); + goto end; + } + struct engine_arg arg[QTFS_MAX_THREADS]; + for (int i = 0; i < thread_nums; i++) { +- arg[i].psize = psize; ++ arg[i].psize = QTFS_USERP_SIZE; + arg[i].fd = fd; + arg[i].thread_idx = i; + (void)pthread_create(&texec[i], NULL, qtfs_engine_kthread, &arg[i]); + } + (void)pthread_create(&tepoll, NULL, qtfs_engine_epoll_thread, &arg[0]); ++ // 必须放在这个位置,uds main里面最终也有join ++ if (uds_proxy_main(6, &argv[1]) != 0) { ++ engine_out("uds proxy start failed."); ++ goto engine_free; ++ } + for (int i = 0; i < thread_nums; i++) { + pthread_join(texec[i], NULL); + engine_out("qtfs engine join thread %d.", i); + } + pthread_join(tepoll, NULL); ++engine_free: + qtfs_engine_userp_free(userp, thread_nums); + engine_out("qtfs engine join epoll thread."); + end: +diff --git a/qtfs/qtinfo/qtinfo.c b/qtfs/qtinfo/qtinfo.c +index dc88da0..a8ba2e0 100644 +--- a/qtfs/qtinfo/qtinfo.c ++++ b/qtfs/qtinfo/qtinfo.c +@@ -4,9 +4,13 @@ + #include + #include + #include ++#include ++#include ++#include + + #include "qtinfo.h" + #include "comm.h" ++#include "ipc/uds_main.h" + + #ifdef client + #define QTFS_DEV_NAME "/dev/qtfs_client" +@@ -312,6 +316,69 @@ void qtinfo_opt_p(int fd, char *support) + return; + } + ++#define PATH_MAX 4096 ++void qtinfo_opt_u() ++{ ++ int len; ++ struct sockaddr_un svr; ++ int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (sockfd < 0) { ++ qtinfo_err("Create socket fd failed."); ++ return; ++ } ++ ++ memset(&svr, 0, sizeof(svr)); ++ svr.sun_family = AF_UNIX; ++ strcpy(svr.sun_path, UDS_DIAG_ADDR); ++ len = offsetof(struct sockaddr_un, sun_path) + strlen(svr.sun_path); ++ if (connect(sockfd, (struct sockaddr *)&svr, len) < 0) { ++ qtinfo_err("connect to %s failed.", UDS_DIAG_ADDR); ++ return; ++ } ++ while (1) { ++ char buf[256]; ++ int n; ++ memset(buf, 0, 256); ++ n = recv(sockfd, buf, 256, 0); ++ if (n <= 0) ++ break; ++ qtinfo_out2("%s", buf); ++ } ++ close(sockfd); ++ return; ++} ++ ++void qtinfo_opt_s() ++{ ++ int len; ++ struct sockaddr_un svr; ++ int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (sockfd < 0) { ++ qtinfo_err("Create socket fd failed."); ++ return; ++ } ++ ++ memset(&svr, 0, sizeof(svr)); ++ svr.sun_family = AF_UNIX; ++ strcpy(svr.sun_path, UDS_LOGLEVEL_UPD); ++ len = offsetof(struct sockaddr_un, sun_path) + strlen(svr.sun_path); ++ if (connect(sockfd, (struct sockaddr *)&svr, len) < 0) { ++ qtinfo_err("connect to %s failed.", UDS_LOGLEVEL_UPD); ++ return; ++ } ++ while (1) { ++ char buf[256]; ++ int n; ++ memset(buf, 0, 256); ++ n = recv(sockfd, buf, 256, 0); ++ if (n <= 0) ++ break; ++ qtinfo_out2("%s", buf); ++ } ++ close(sockfd); ++ return; ++ ++} + + static void qtinfo_help(char *exec) + { +@@ -322,6 +389,8 @@ static void qtinfo_help(char *exec) + qtinfo_out(" -l, Set log level(valid param: \"NONE\", \"ERROR\", \"WARN\", \"INFO\", \"DEBUG\")."); + qtinfo_out(" -t, For test informations."); + qtinfo_out(" -p, Epoll support file mode(1: any files; 0: only fifo)."); ++ qtinfo_out(" -u, Display unix socket proxy diagnostic info"); ++ qtinfo_out(" -s, Set unix socket proxy log level(Increase by 1 each time)"); + } + + int main(int argc, char *argv[]) +@@ -334,7 +403,7 @@ int main(int argc, char *argv[]) + qtinfo_err("open file %s failed.", QTFS_DEV_NAME); + return 0; + } +- while ((ch = getopt(argc, argv, "acl:tp:")) != -1) { ++ while ((ch = getopt(argc, argv, "acl:tp:us")) != -1) { + switch (ch) { + case 'a': + qtinfo_opt_a(fd); +@@ -351,6 +420,12 @@ int main(int argc, char *argv[]) + case 'p': + qtinfo_opt_p(fd, optarg); + break; ++ case 'u': ++ qtinfo_opt_u(); ++ break; ++ case 's': ++ qtinfo_opt_s(); ++ break; + default: + qtinfo_help(argv[0]); + break; +diff --git a/qtfs/qtsock.c b/qtfs/qtsock.c +new file mode 100644 +index 0000000..58b2eab +--- /dev/null ++++ b/qtfs/qtsock.c +@@ -0,0 +1,332 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "conn.h" ++#include "log.h" ++#include "comm.h" ++#include "qtfs/syscall.h" ++ ++#define MAX_SOCK_PATH_LEN 108 ++ ++static struct socket *qtfs_sock = NULL; ++static struct mutex qtfs_sock_mutex; ++static char qtfs_sock_path[] = "/var/run/qtfs/remote_uds.sock"; ++ ++struct qtsock_wl_stru qtsock_wl; ++ ++static struct sock *(*origin_unix_find_other)(struct net *net, ++ struct sockaddr_un *sunname, int len, ++ int type, unsigned int hash, int *error); ++ ++struct ftrace_hook { ++ const char *name; ++ void *func; ++ void *origin; ++ ++ unsigned long addr; ++ struct ftrace_ops ops; ++}; ++ ++struct ftrace_hook unix_find_other_hook; ++ ++static int resolve_hook_address(struct ftrace_hook *hook) ++{ ++ hook->addr = qtfs_kallsyms_lookup_name(hook->name); ++ if (!hook->addr) { ++ qtfs_warn("unresolved symbol during resolving hook address:%s\n", hook->name); ++ return -ENOENT; ++ } ++ *((unsigned long *)hook->origin) = hook->addr; ++ ++ return 0; ++} ++ ++static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, ++ struct ftrace_ops *ops, struct pt_regs *regs) ++{ ++ struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); ++ ++ if (!within_module(parent_ip, THIS_MODULE)) ++ regs->ip = (unsigned long)hook->func; ++} ++ ++int install_hook(struct ftrace_hook *hook) ++{ ++ int err; ++ ++ err = resolve_hook_address(hook); ++ if (err) ++ return err; ++ ++ hook->ops.func = ftrace_thunk; ++ hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY; ++ ++ err = ftrace_set_filter_ip(&hook->ops, hook->addr, 0, 0); ++ if (err) { ++ qtfs_err("ftrace_set_filter_ip failed:%d\n", err); ++ return err; ++ } ++ ++ err = register_ftrace_function(&hook->ops); ++ if (err) { ++ qtfs_err("register_ftrace_function failed with :%d\n", err); ++ ftrace_set_filter_ip(&hook->ops, hook->addr, 1, 0); ++ return err; ++ } ++ qtfs_info("install hook(%s) done\n", hook->name); ++ ++ return 0; ++} ++ ++void remove_hook(struct ftrace_hook *hook) ++{ ++ int err; ++ ++ err = unregister_ftrace_function(&hook->ops); ++ if (err) ++ qtfs_err("unregister_ftrace_function failed:%d\n", err); ++ ++ err = ftrace_set_filter_ip(&hook->ops, hook->addr, 1, 0); ++ if (err) ++ qtfs_err("ftrace_set_filter_ip failed:%d\n", err); ++ qtfs_info("remove hook(%s) done", hook->name); ++} ++ ++struct qtfs_sock_req { ++ int magic; ++ int type; ++ char sunname[MAX_SOCK_PATH_LEN]; ++}; ++ ++struct qtfs_sock_rsp { ++ int found; ++}; ++ ++static int qtsock_conn(void) ++{ ++ int ret; ++ struct sockaddr_un saddr; ++ ++ ret = mutex_lock_interruptible(&qtfs_sock_mutex); ++ if (ret <0) { ++ qtfs_err("Failed to get qtfs sock mutex lock:%d\n", ret); ++ return false; ++ } ++ // calling this function means qtfs_sock isn't working properly. ++ // so it's ok to release and clean old qtfs_sock ++ if (qtfs_sock) { ++ sock_release(qtfs_sock); ++ qtfs_sock = NULL; ++ } ++ // connect to userspace unix socket server ++ ret = __sock_create(&init_net, AF_UNIX, SOCK_STREAM, 0, &qtfs_sock, 1); ++ if (ret) { ++ qtfs_err("qtfs sock client init create sock failed:%d\n", ret); ++ mutex_unlock(&qtfs_sock_mutex); ++ return ret; ++ } ++ saddr.sun_family = PF_UNIX; ++ strcpy(saddr.sun_path, qtfs_sock_path); ++ ret = qtfs_sock->ops->connect(qtfs_sock, (struct sockaddr *)&saddr, ++ sizeof(struct sockaddr_un) - 1, 0); ++ if (ret) { ++ qtfs_err("qtfs sock client sock connect failed:%d\n", ret); ++ sock_release(qtfs_sock); ++ qtfs_sock = NULL; ++ mutex_unlock(&qtfs_sock_mutex); ++ return ret; ++ } ++ ++ mutex_unlock(&qtfs_sock_mutex); ++ return ret; ++} ++ ++bool qtfs_udsfind(char *sunname, int len, int type) ++{ ++ struct qtfs_sock_req qs_req; ++ struct qtfs_sock_rsp qs_rsp; ++ struct kvec send_vec, recv_vec; ++ struct msghdr send_msg, recv_msg; ++ int ret; ++ int retry = 0, penalty = 100, i = 0; ++ ++ // qtfs_sock still not initialized, try to connect to server ++ if (!qtfs_sock && (qtsock_conn() < 0)) { ++ qtfs_err("failed to connect to qtfs socket\n"); ++ return false; ++ } ++ if (len > MAX_SOCK_PATH_LEN) { ++ qtfs_err("Invalid socket path name len(%d)\n", len); ++ return false; ++ } ++ memset(&qs_req, 0, sizeof(qs_req)); ++ memset(&qs_rsp, 0, sizeof(qs_rsp)); ++ strncpy(qs_req.sunname, sunname, len); ++ qs_req.type = type; ++ qs_req.magic = 0xDEADBEEF; ++ ++ memset(&send_msg, 0, sizeof(send_msg)); ++ memset(&send_vec, 0, sizeof(send_vec)); ++ memset(&recv_msg, 0, sizeof(recv_msg)); ++ memset(&recv_vec, 0, sizeof(recv_vec)); ++ ++ send_vec.iov_base = &qs_req; ++ send_vec.iov_len = sizeof(qs_req); ++ qtfs_info("qtfs uds find socket(%s), type(%d)\n", sunname, type); ++ ++reconn: ++ if (retry) { ++ for (i = 0; i < retry; i++) { ++ if (qtsock_conn() == 0) ++ break; ++ qtfs_err("qtfs socket reconnect failed for %d trial", i+1); ++ penalty *= 2; ++ msleep(penalty); ++ } ++ } ++ ret = mutex_lock_interruptible(&qtfs_sock_mutex); ++ if (ret < 0) { ++ qtfs_err("Failed to get qtfs sock mutex lock:%d\n", ret); ++ return false; ++ } ++ if (!qtfs_sock) { ++ qtfs_err("qtfs_sock is NULL, please check\n"); ++ mutex_unlock(&qtfs_sock_mutex); ++ return false; ++ } ++ send_msg.msg_flags |= MSG_NOSIGNAL; ++ ret = kernel_sendmsg(qtfs_sock, &send_msg, &send_vec, 1, sizeof(qs_req)); ++ if (ret == -EPIPE && retry == 0) { ++ qtfs_err("uds find connection has broken, try to reconnect\n"); ++ retry = 3; ++ mutex_unlock(&qtfs_sock_mutex); ++ goto reconn; ++ } else if (ret < 0) { ++ qtfs_err("Failed to send uds find message:%d\n", ret); ++ mutex_unlock(&qtfs_sock_mutex); ++ return false; ++ } ++ ++ // waiting for response ++ recv_vec.iov_base = &qs_rsp; ++ recv_vec.iov_len = sizeof(qs_rsp); ++retry: ++ recv_msg.msg_flags |= MSG_NOSIGNAL; ++ ret = kernel_recvmsg(qtfs_sock, &recv_msg, &recv_vec, 1, sizeof(qs_rsp), 0); ++ if (ret == -ERESTARTSYS || ret == -EINTR) { ++ qtfs_err("uds remote find get interrupted, just retry"); ++ msleep(1); ++ goto retry; ++ } ++ mutex_unlock(&qtfs_sock_mutex); ++ if (ret < 0) { ++ qtfs_err("Failed to receive uds find response:%d\n", ret); ++ return false; ++ } ++ qtfs_info("uds remote find socket(%s), type(%d), result:%s\n", sunname, type, qs_rsp.found ? "found" : "not found"); ++ return qs_rsp.found; ++} ++ ++static int uds_find_whitelist(const char *path) ++{ ++ int i; ++ int ret = 1; ++ read_lock(&qtsock_wl.rwlock); ++ for (i = 0; i< qtsock_wl.nums; i++) { ++ if (strncmp(path, qtsock_wl.wl[i], strlen(qtsock_wl.wl[i])) == 0) { ++ ret = 0; ++ break; ++ } ++ } ++ read_unlock(&qtsock_wl.rwlock); ++ return ret; ++} ++ ++static inline bool uds_is_proxy(void) ++{ ++ return (current->tgid == qtfs_uds_proxy_pid); ++} ++ ++static struct sock *qtfs_unix_find_other(struct net *net, ++ struct sockaddr_un *sunname, int len, ++ int type, unsigned int hash, int *error) ++{ ++ struct sock *other = NULL; ++ bool found = false; ++ ++ qtfs_debug("in qtfs_unix_find_other (%s)\n", sunname->sun_path); ++ other = origin_unix_find_other(net, sunname, len, type, hash, error); ++ if (other) { ++ qtfs_debug("find unix other sock(%s) locally", sunname->sun_path); ++ return other; ++ } ++ ++ // do not call remote find if sunname is annomous or sunpath not in whitelist ++ if (!sunname->sun_path[0] || uds_find_whitelist(sunname->sun_path) || ++ uds_is_proxy() == true) { ++ *error = -ECONNREFUSED; ++ return NULL; ++ } ++ ++ qtfs_info("Failed to find unix other sock(%s) locally, try to find remotely\n", sunname->sun_path); ++ // refer userspace service to get remote socket status ++ // if found, which means userspace service has create this unix socket server, just go to origin_unix_find_other, it will be found ++ // if not found, return NULL ++ found = qtfs_udsfind(sunname->sun_path, len, type); ++ if (!found) { ++ qtfs_info("failed to find unix other sock(%s) remotely", sunname->sun_path); ++ *error = -ECONNREFUSED; ++ return NULL; ++ } ++ qtfs_info("find unix other sock(%s) remotely\n", sunname->sun_path); ++ ++ // found it remotely, so we will inform userspace engine to create specfic unix socket and connect to qtfs server ++ // and call unix_find_other locally ++ // xxx: will this be called recursively? Hope not ++ return origin_unix_find_other(net, sunname, len, type, hash, error); ++} ++ ++int qtfs_sock_init(void) ++{ ++ qtfs_kallsyms_hack_init(); ++ ++ qtfs_info("in qtfs ftrace hook unix_find_other\n"); ++ unix_find_other_hook.name = "unix_find_other"; ++ unix_find_other_hook.func = qtfs_unix_find_other; ++ unix_find_other_hook.origin = &origin_unix_find_other; ++ ++ install_hook(&unix_find_other_hook); ++ mutex_init(&qtfs_sock_mutex); ++ rwlock_init(&qtsock_wl.rwlock); ++ qtsock_wl.nums = 0; ++ qtsock_wl.wl = (char **)kmalloc(sizeof(char *) * QTSOCK_WL_MAX_NUM, GFP_KERNEL); ++ if (qtsock_wl.wl == NULL) { ++ ++ qtfs_err("failed to kmalloc wl, max num:%d", QTSOCK_WL_MAX_NUM); ++ } ++ ++ return 0; ++} ++ ++void qtfs_sock_exit(void) ++{ ++ int ret; ++ qtfs_info("exit qtfs ftrace, remove unix_find_other_hook\n"); ++ remove_hook(&unix_find_other_hook); ++ ++ ret = mutex_lock_interruptible(&qtfs_sock_mutex); ++ if (ret < 0) ++ qtfs_err("Failed to get qtfs sock mutex lock:%d\n", ret); ++ // close unix socket connected to userspace ++ if (qtfs_sock) { ++ sock_release(qtfs_sock); ++ qtfs_sock = NULL; ++ } ++ mutex_unlock(&qtfs_sock_mutex); ++} +-- +2.33.0 + diff --git a/0009-Add-rexec-shim.patch b/0009-Add-rexec-shim.patch new file mode 100644 index 0000000..3a04c9d --- /dev/null +++ b/0009-Add-rexec-shim.patch @@ -0,0 +1,3954 @@ +From 0a219b5916028f8333bf44563fa7cf17e8f07879 Mon Sep 17 00:00:00 2001 +From: yangxin <245051644@qq.com> +Date: Fri, 10 Feb 2023 17:02:59 +0800 +Subject: [PATCH 5/5] Add rexec shim. + +Signed-off-by: yangxin <245051644@qq.com> +--- + qtfs/rexec/Makefile | 21 +- + qtfs/rexec/client.go | 8 +- + qtfs/rexec/common.go | 11 +- + qtfs/rexec/fd.go | 122 ++ + qtfs/rexec/rshim/Makefile | 10 + + qtfs/rexec/rshim/cJSON.c | 3119 +++++++++++++++++++++++++++++++++ + qtfs/rexec/rshim/cJSON.h | 300 ++++ + qtfs/rexec/rshim/rexec_shim.c | 198 +++ + qtfs/rexec/server.go | 11 +- + 9 files changed, 3788 insertions(+), 12 deletions(-) + create mode 100644 qtfs/rexec/fd.go + create mode 100644 qtfs/rexec/rshim/Makefile + create mode 100644 qtfs/rexec/rshim/cJSON.c + create mode 100644 qtfs/rexec/rshim/cJSON.h + create mode 100644 qtfs/rexec/rshim/rexec_shim.c + +diff --git a/qtfs/rexec/Makefile b/qtfs/rexec/Makefile +index 86d4f78..96fbdb0 100644 +--- a/qtfs/rexec/Makefile ++++ b/qtfs/rexec/Makefile +@@ -1,11 +1,22 @@ +-all: rexec rexec_server ++all: rexec rexec_server rexec_shim + +-rexec : client.go common.go +- go build -o rexec client.go common.go ++rexec : client.go common.go fd.go ++ go build -o rexec client.go common.go fd.go + +-rexec_server : server.go common.go +- go build -o rexec_server server.go common.go ++rexec_server : server.go common.go fd.go ++ go build -o rexec_server server.go common.go fd.go + test: + go test -v ./common_test.go ./common.go ++ ++rexec_shim: ++ $(MAKE) -C ./rshim ++ ++install: ++ cp -f rexec /usr/bin/ ++ cp -f rexec /usr/bin/ ++ $(MAKE) install -C ./rshim ++ + clean: + rm -rf rexec rexec_server ++ $(MAKE) clean -C ./rshim ++ +diff --git a/qtfs/rexec/client.go b/qtfs/rexec/client.go +index dc1af8b..55835de 100644 +--- a/qtfs/rexec/client.go ++++ b/qtfs/rexec/client.go +@@ -8,6 +8,7 @@ import ( + "os/signal" + "path/filepath" + "strconv" ++ "strings" + "syscall" + "time" + +@@ -61,6 +62,9 @@ func removePidFile() { + + func cleanRedundantPidFile() { + filepath.Walk(rexecPidDir, func(path string, info os.FileInfo, err error) error { ++ if strings.TrimSuffix(path, "/") == strings.TrimSuffix(rexecPidDir, "/") { ++ return nil ++ } + f, err := os.Open(path) + if err != nil { + // open failed, just skip +@@ -147,7 +151,9 @@ func main() { + Stderr: os.Stderr, + Env: append([]string{}, os.Environ()...), + StatusChan: remoteSender, ++ Files: make(map[int]FileInfo), + } ++ checkpointFileInfo(command.Files) + + err = sender.Send(command) + if err != nil { +@@ -168,7 +174,7 @@ retry: + log.Fatal(err) + } + if (response.WhiteList == 0) { +- log.Fatalf("%s command in White List of rexec server\n", command.Cmd) ++ log.Fatalf("%s command not in White List of rexec server\n", command.Cmd) + } + pid := response.Pid + lpid := os.Getpid() +diff --git a/qtfs/rexec/common.go b/qtfs/rexec/common.go +index b59b12b..a74c32b 100644 +--- a/qtfs/rexec/common.go ++++ b/qtfs/rexec/common.go +@@ -1,16 +1,16 @@ + package main + + import ( ++ "encoding/json" + "fmt" + "io" ++ "io/ioutil" + "net" + "net/url" + "os" + "strconv" + "strings" + "syscall" +- "io/ioutil" +- "encoding/json" + + "github.com/docker/libchan" + ) +@@ -29,6 +29,7 @@ type RemoteCommand struct { + Stderr io.WriteCloser + StatusChan libchan.Sender + Cgroups map[string]string ++ Files map[int]FileInfo + } + + func CheckRight(fileName string) error { +@@ -43,11 +44,11 @@ func CheckRight(fileName string) error { + gid = int(stat.Gid) + mode = int(stat.Mode) + +- if (uid != 0 || gid != 0) { ++ if uid != 0 || gid != 0 { + return fmt.Errorf("Owner of %s must be root\n", fileName) + } + +- if (mode & 0777 != 0400) { ++ if mode & 0777 != 0400 { + return fmt.Errorf("Mode of %s must be 0400\n", fileName) + } + +@@ -113,7 +114,7 @@ func parseUnixAddr(inAddr string) (NetAddr, error) { + }, nil + } + +-func readAddrFromFile(role string) (string) { ++func readAddrFromFile(role string) string { + fileName := fmt.Sprintf("%s/%s.json", configDir, role) + if err := CheckRight(fileName); err != nil { + fmt.Printf("Check right of %s failed: %s", fileName, err) +diff --git a/qtfs/rexec/fd.go b/qtfs/rexec/fd.go +new file mode 100644 +index 0000000..acff8d4 +--- /dev/null ++++ b/qtfs/rexec/fd.go +@@ -0,0 +1,122 @@ ++package main ++ ++import ( ++ "encoding/json" ++ "fmt" ++ "io/ioutil" ++ "log" ++ "math/rand" ++ "os" ++ "path/filepath" ++ "sort" ++ "strconv" ++ "strings" ++) ++ ++const FdPath = "/var/run/rexec/fds/" ++ ++type FileInfo struct { ++ Fd int ++ Path string ++ Perm int ++ Offset int ++} ++ ++type Files struct { ++ Files []FileInfo ++} ++ ++var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") ++ ++func randString(n int) string { ++ b := make([]rune, n) ++ letterLen := len(defaultLetters) ++ ++ for i := range b { ++ b[i] = defaultLetters[rand.Intn(letterLen)] ++ } ++ ++ return string(b) ++} ++ ++func getPosAndFlags(path string) (int, int, error) { ++ content, err := ioutil.ReadFile(path) ++ if err != nil { ++ log.Printf("failed to read file(%s):%s", path, err.Error()) ++ return 0, 0, err ++ } ++ var pos, flags int ++ fmt.Sscanf(string(content), "pos:%d\nflags:%o", &pos, &flags) ++ return pos, flags, nil ++} ++ ++func checkpointFileInfo(fdMaps map[int]FileInfo) { ++ procPath := "/proc/self" ++ fdPath := procPath + "/fd/" ++ fdinfoPath := procPath + "/fdinfo/" ++ ++ filepath.Walk(fdPath, func(path string, fi os.FileInfo, err error) error { ++ if fi == nil || err != nil { ++ log.Printf("path %s failed with %s\n", path, err.Error()) ++ return nil ++ } ++ fdstr := strings.TrimPrefix(path, fdPath) ++ if fdstr == "" { ++ return nil ++ } ++ fd, err := strconv.Atoi(fdstr) ++ if err != nil { ++ log.Printf("convert fd string(%s) to int failed: %s\n", fdstr, err.Error()) ++ return nil ++ } ++ ++ linkPath, err := os.Readlink(path) ++ if err != nil { ++ log.Printf("readlink (%s) failed with: %s\n", path, err.Error()) ++ return nil ++ } ++ // skip stdin/stdout/stderr or non-regular files ++ if fd < 3 || !strings.HasPrefix(linkPath, "/") { ++ return nil ++ } ++ ++ pos, flags, err := getPosAndFlags(fdinfoPath + fdstr) ++ if err != nil { ++ return nil ++ } ++ fdMaps[fd] = FileInfo{ ++ Fd: fd, ++ Path: linkPath, ++ Perm: flags, ++ Offset: pos, ++ } ++ return nil ++ }) ++} ++ ++func restoreFileInfo(cmd string, fdMaps map[int]FileInfo) string { ++ var fds []int ++ var fs Files ++ ++ for fd := range fdMaps { ++ fds = append(fds, fd) ++ } ++ ++ sort.Ints(fds) ++ fs.Files = []FileInfo{} ++ for _, fd := range fds { ++ fs.Files = append(fs.Files, fdMaps[fd]) ++ } ++ js, err := json.Marshal(fs) ++ if err != nil { ++ return "" ++ } ++ os.MkdirAll(FdPath, os.ModePerm) ++ _, cmdName := filepath.Split(cmd) ++ fName := FdPath + cmdName + "-" + randString(20) + ".json" ++ if err := ioutil.WriteFile(fName, js, 0640); err != nil { ++ log.Printf("write %s faild with error: %s\n", fName, err.Error()) ++ return "" ++ } ++ return fName ++} +diff --git a/qtfs/rexec/rshim/Makefile b/qtfs/rexec/rshim/Makefile +new file mode 100644 +index 0000000..6e28911 +--- /dev/null ++++ b/qtfs/rexec/rshim/Makefile +@@ -0,0 +1,10 @@ ++all: rexec_shim ++ ++rexec_shim: ++ gcc -g -O2 -o rexec_shim rexec_shim.c cJSON.c ++ ++install: ++ cp rexec_shim /usr/bin/ ++ ++clean: ++ rm -rf rexec_shim *.o +diff --git a/qtfs/rexec/rshim/cJSON.c b/qtfs/rexec/rshim/cJSON.c +new file mode 100644 +index 0000000..524ba46 +--- /dev/null ++++ b/qtfs/rexec/rshim/cJSON.c +@@ -0,0 +1,3119 @@ ++/* ++ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors ++ ++ Permission is hereby granted, free of charge, to any person obtaining a copy ++ of this software and associated documentation files (the "Software"), to deal ++ in the Software without restriction, including without limitation the rights ++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ copies of the Software, and to permit persons to whom the Software is ++ furnished to do so, subject to the following conditions: ++ ++ The above copyright notice and this permission notice shall be included in ++ all copies or substantial portions of the Software. ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ THE SOFTWARE. ++*/ ++ ++/* cJSON */ ++/* JSON parser in C. */ ++ ++/* disable warnings about old C89 functions in MSVC */ ++#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) ++#define _CRT_SECURE_NO_DEPRECATE ++#endif ++ ++#ifdef __GNUC__ ++#pragma GCC visibility push(default) ++#endif ++#if defined(_MSC_VER) ++#pragma warning (push) ++/* disable warning about single line comments in system headers */ ++#pragma warning (disable : 4001) ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef ENABLE_LOCALES ++#include ++#endif ++ ++#if defined(_MSC_VER) ++#pragma warning (pop) ++#endif ++#ifdef __GNUC__ ++#pragma GCC visibility pop ++#endif ++ ++#include "cJSON.h" ++ ++/* define our own boolean type */ ++#ifdef true ++#undef true ++#endif ++#define true ((cJSON_bool)1) ++ ++#ifdef false ++#undef false ++#endif ++#define false ((cJSON_bool)0) ++ ++/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ ++#ifndef isinf ++#define isinf(d) (isnan((d - d)) && !isnan(d)) ++#endif ++#ifndef isnan ++#define isnan(d) (d != d) ++#endif ++ ++#ifndef NAN ++#ifdef _WIN32 ++#define NAN sqrt(-1.0) ++#else ++#define NAN 0.0/0.0 ++#endif ++#endif ++ ++typedef struct { ++ const unsigned char *json; ++ size_t position; ++} error; ++static error global_error = { NULL, 0 }; ++ ++CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) ++{ ++ return (const char*) (global_error.json + global_error.position); ++} ++ ++CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) ++{ ++ if (!cJSON_IsString(item)) ++ { ++ return NULL; ++ } ++ ++ return item->valuestring; ++} ++ ++CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) ++{ ++ if (!cJSON_IsNumber(item)) ++ { ++ return (double) NAN; ++ } ++ ++ return item->valuedouble; ++} ++ ++/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ ++#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) ++ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. ++#endif ++ ++CJSON_PUBLIC(const char*) cJSON_Version(void) ++{ ++ static char version[15]; ++ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); ++ ++ return version; ++} ++ ++/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ ++static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) ++{ ++ if ((string1 == NULL) || (string2 == NULL)) ++ { ++ return 1; ++ } ++ ++ if (string1 == string2) ++ { ++ return 0; ++ } ++ ++ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) ++ { ++ if (*string1 == '\0') ++ { ++ return 0; ++ } ++ } ++ ++ return tolower(*string1) - tolower(*string2); ++} ++ ++typedef struct internal_hooks ++{ ++ void *(CJSON_CDECL *allocate)(size_t size); ++ void (CJSON_CDECL *deallocate)(void *pointer); ++ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); ++} internal_hooks; ++ ++#if defined(_MSC_VER) ++/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ ++static void * CJSON_CDECL internal_malloc(size_t size) ++{ ++ return malloc(size); ++} ++static void CJSON_CDECL internal_free(void *pointer) ++{ ++ free(pointer); ++} ++static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) ++{ ++ return realloc(pointer, size); ++} ++#else ++#define internal_malloc malloc ++#define internal_free free ++#define internal_realloc realloc ++#endif ++ ++/* strlen of character literals resolved at compile time */ ++#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) ++ ++static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; ++ ++static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) ++{ ++ size_t length = 0; ++ unsigned char *copy = NULL; ++ ++ if (string == NULL) ++ { ++ return NULL; ++ } ++ ++ length = strlen((const char*)string) + sizeof(""); ++ copy = (unsigned char*)hooks->allocate(length); ++ if (copy == NULL) ++ { ++ return NULL; ++ } ++ memcpy(copy, string, length); ++ ++ return copy; ++} ++ ++CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) ++{ ++ if (hooks == NULL) ++ { ++ /* Reset hooks */ ++ global_hooks.allocate = malloc; ++ global_hooks.deallocate = free; ++ global_hooks.reallocate = realloc; ++ return; ++ } ++ ++ global_hooks.allocate = malloc; ++ if (hooks->malloc_fn != NULL) ++ { ++ global_hooks.allocate = hooks->malloc_fn; ++ } ++ ++ global_hooks.deallocate = free; ++ if (hooks->free_fn != NULL) ++ { ++ global_hooks.deallocate = hooks->free_fn; ++ } ++ ++ /* use realloc only if both free and malloc are used */ ++ global_hooks.reallocate = NULL; ++ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) ++ { ++ global_hooks.reallocate = realloc; ++ } ++} ++ ++/* Internal constructor. */ ++static cJSON *cJSON_New_Item(const internal_hooks * const hooks) ++{ ++ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); ++ if (node) ++ { ++ memset(node, '\0', sizeof(cJSON)); ++ } ++ ++ return node; ++} ++ ++/* Delete a cJSON structure. */ ++CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) ++{ ++ cJSON *next = NULL; ++ while (item != NULL) ++ { ++ next = item->next; ++ if (!(item->type & cJSON_IsReference) && (item->child != NULL)) ++ { ++ cJSON_Delete(item->child); ++ } ++ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) ++ { ++ global_hooks.deallocate(item->valuestring); ++ } ++ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) ++ { ++ global_hooks.deallocate(item->string); ++ } ++ global_hooks.deallocate(item); ++ item = next; ++ } ++} ++ ++/* get the decimal point character of the current locale */ ++static unsigned char get_decimal_point(void) ++{ ++#ifdef ENABLE_LOCALES ++ struct lconv *lconv = localeconv(); ++ return (unsigned char) lconv->decimal_point[0]; ++#else ++ return '.'; ++#endif ++} ++ ++typedef struct ++{ ++ const unsigned char *content; ++ size_t length; ++ size_t offset; ++ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ ++ internal_hooks hooks; ++} parse_buffer; ++ ++/* check if the given size is left to read in a given parse buffer (starting with 1) */ ++#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) ++/* check if the buffer can be accessed at the given index (starting with 0) */ ++#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) ++#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) ++/* get a pointer to the buffer at the position */ ++#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) ++ ++/* Parse the input text to generate a number, and populate the result into item. */ ++static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) ++{ ++ double number = 0; ++ unsigned char *after_end = NULL; ++ unsigned char number_c_string[64]; ++ unsigned char decimal_point = get_decimal_point(); ++ size_t i = 0; ++ ++ if ((input_buffer == NULL) || (input_buffer->content == NULL)) ++ { ++ return false; ++ } ++ ++ /* copy the number into a temporary buffer and replace '.' with the decimal point ++ * of the current locale (for strtod) ++ * This also takes care of '\0' not necessarily being available for marking the end of the input */ ++ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) ++ { ++ switch (buffer_at_offset(input_buffer)[i]) ++ { ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case '+': ++ case '-': ++ case 'e': ++ case 'E': ++ number_c_string[i] = buffer_at_offset(input_buffer)[i]; ++ break; ++ ++ case '.': ++ number_c_string[i] = decimal_point; ++ break; ++ ++ default: ++ goto loop_end; ++ } ++ } ++loop_end: ++ number_c_string[i] = '\0'; ++ ++ number = strtod((const char*)number_c_string, (char**)&after_end); ++ if (number_c_string == after_end) ++ { ++ return false; /* parse_error */ ++ } ++ ++ item->valuedouble = number; ++ ++ /* use saturation in case of overflow */ ++ if (number >= INT_MAX) ++ { ++ item->valueint = INT_MAX; ++ } ++ else if (number <= (double)INT_MIN) ++ { ++ item->valueint = INT_MIN; ++ } ++ else ++ { ++ item->valueint = (int)number; ++ } ++ ++ item->type = cJSON_Number; ++ ++ input_buffer->offset += (size_t)(after_end - number_c_string); ++ return true; ++} ++ ++/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ ++CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) ++{ ++ if (number >= INT_MAX) ++ { ++ object->valueint = INT_MAX; ++ } ++ else if (number <= (double)INT_MIN) ++ { ++ object->valueint = INT_MIN; ++ } ++ else ++ { ++ object->valueint = (int)number; ++ } ++ ++ return object->valuedouble = number; ++} ++ ++CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) ++{ ++ char *copy = NULL; ++ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ ++ if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) ++ { ++ return NULL; ++ } ++ if (strlen(valuestring) <= strlen(object->valuestring)) ++ { ++ strcpy(object->valuestring, valuestring); ++ return object->valuestring; ++ } ++ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); ++ if (copy == NULL) ++ { ++ return NULL; ++ } ++ if (object->valuestring != NULL) ++ { ++ cJSON_free(object->valuestring); ++ } ++ object->valuestring = copy; ++ ++ return copy; ++} ++ ++typedef struct ++{ ++ unsigned char *buffer; ++ size_t length; ++ size_t offset; ++ size_t depth; /* current nesting depth (for formatted printing) */ ++ cJSON_bool noalloc; ++ cJSON_bool format; /* is this print a formatted print */ ++ internal_hooks hooks; ++} printbuffer; ++ ++/* realloc printbuffer if necessary to have at least "needed" bytes more */ ++static unsigned char* ensure(printbuffer * const p, size_t needed) ++{ ++ unsigned char *newbuffer = NULL; ++ size_t newsize = 0; ++ ++ if ((p == NULL) || (p->buffer == NULL)) ++ { ++ return NULL; ++ } ++ ++ if ((p->length > 0) && (p->offset >= p->length)) ++ { ++ /* make sure that offset is valid */ ++ return NULL; ++ } ++ ++ if (needed > INT_MAX) ++ { ++ /* sizes bigger than INT_MAX are currently not supported */ ++ return NULL; ++ } ++ ++ needed += p->offset + 1; ++ if (needed <= p->length) ++ { ++ return p->buffer + p->offset; ++ } ++ ++ if (p->noalloc) { ++ return NULL; ++ } ++ ++ /* calculate new buffer size */ ++ if (needed > (INT_MAX / 2)) ++ { ++ /* overflow of int, use INT_MAX if possible */ ++ if (needed <= INT_MAX) ++ { ++ newsize = INT_MAX; ++ } ++ else ++ { ++ return NULL; ++ } ++ } ++ else ++ { ++ newsize = needed * 2; ++ } ++ ++ if (p->hooks.reallocate != NULL) ++ { ++ /* reallocate with realloc if available */ ++ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); ++ if (newbuffer == NULL) ++ { ++ p->hooks.deallocate(p->buffer); ++ p->length = 0; ++ p->buffer = NULL; ++ ++ return NULL; ++ } ++ } ++ else ++ { ++ /* otherwise reallocate manually */ ++ newbuffer = (unsigned char*)p->hooks.allocate(newsize); ++ if (!newbuffer) ++ { ++ p->hooks.deallocate(p->buffer); ++ p->length = 0; ++ p->buffer = NULL; ++ ++ return NULL; ++ } ++ ++ memcpy(newbuffer, p->buffer, p->offset + 1); ++ p->hooks.deallocate(p->buffer); ++ } ++ p->length = newsize; ++ p->buffer = newbuffer; ++ ++ return newbuffer + p->offset; ++} ++ ++/* calculate the new length of the string in a printbuffer and update the offset */ ++static void update_offset(printbuffer * const buffer) ++{ ++ const unsigned char *buffer_pointer = NULL; ++ if ((buffer == NULL) || (buffer->buffer == NULL)) ++ { ++ return; ++ } ++ buffer_pointer = buffer->buffer + buffer->offset; ++ ++ buffer->offset += strlen((const char*)buffer_pointer); ++} ++ ++/* securely comparison of floating-point variables */ ++static cJSON_bool compare_double(double a, double b) ++{ ++ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); ++ return (fabs(a - b) <= maxVal * DBL_EPSILON); ++} ++ ++/* Render the number nicely from the given item into a string. */ ++static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) ++{ ++ unsigned char *output_pointer = NULL; ++ double d = item->valuedouble; ++ int length = 0; ++ size_t i = 0; ++ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ ++ unsigned char decimal_point = get_decimal_point(); ++ double test = 0.0; ++ ++ if (output_buffer == NULL) ++ { ++ return false; ++ } ++ ++ /* This checks for NaN and Infinity */ ++ if (isnan(d) || isinf(d)) ++ { ++ length = sprintf((char*)number_buffer, "null"); ++ } ++ else if(d == (double)item->valueint) ++ { ++ length = sprintf((char*)number_buffer, "%d", item->valueint); ++ } ++ else ++ { ++ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ ++ length = sprintf((char*)number_buffer, "%1.15g", d); ++ ++ /* Check whether the original double can be recovered */ ++ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) ++ { ++ /* If not, print with 17 decimal places of precision */ ++ length = sprintf((char*)number_buffer, "%1.17g", d); ++ } ++ } ++ ++ /* sprintf failed or buffer overrun occurred */ ++ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) ++ { ++ return false; ++ } ++ ++ /* reserve appropriate space in the output */ ++ output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ ++ /* copy the printed number to the output and replace locale ++ * dependent decimal point with '.' */ ++ for (i = 0; i < ((size_t)length); i++) ++ { ++ if (number_buffer[i] == decimal_point) ++ { ++ output_pointer[i] = '.'; ++ continue; ++ } ++ ++ output_pointer[i] = number_buffer[i]; ++ } ++ output_pointer[i] = '\0'; ++ ++ output_buffer->offset += (size_t)length; ++ ++ return true; ++} ++ ++/* parse 4 digit hexadecimal number */ ++static unsigned parse_hex4(const unsigned char * const input) ++{ ++ unsigned int h = 0; ++ size_t i = 0; ++ ++ for (i = 0; i < 4; i++) ++ { ++ /* parse digit */ ++ if ((input[i] >= '0') && (input[i] <= '9')) ++ { ++ h += (unsigned int) input[i] - '0'; ++ } ++ else if ((input[i] >= 'A') && (input[i] <= 'F')) ++ { ++ h += (unsigned int) 10 + input[i] - 'A'; ++ } ++ else if ((input[i] >= 'a') && (input[i] <= 'f')) ++ { ++ h += (unsigned int) 10 + input[i] - 'a'; ++ } ++ else /* invalid */ ++ { ++ return 0; ++ } ++ ++ if (i < 3) ++ { ++ /* shift left to make place for the next nibble */ ++ h = h << 4; ++ } ++ } ++ ++ return h; ++} ++ ++/* converts a UTF-16 literal to UTF-8 ++ * A literal can be one or two sequences of the form \uXXXX */ ++static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) ++{ ++ long unsigned int codepoint = 0; ++ unsigned int first_code = 0; ++ const unsigned char *first_sequence = input_pointer; ++ unsigned char utf8_length = 0; ++ unsigned char utf8_position = 0; ++ unsigned char sequence_length = 0; ++ unsigned char first_byte_mark = 0; ++ ++ if ((input_end - first_sequence) < 6) ++ { ++ /* input ends unexpectedly */ ++ goto fail; ++ } ++ ++ /* get the first utf16 sequence */ ++ first_code = parse_hex4(first_sequence + 2); ++ ++ /* check that the code is valid */ ++ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) ++ { ++ goto fail; ++ } ++ ++ /* UTF16 surrogate pair */ ++ if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) ++ { ++ const unsigned char *second_sequence = first_sequence + 6; ++ unsigned int second_code = 0; ++ sequence_length = 12; /* \uXXXX\uXXXX */ ++ ++ if ((input_end - second_sequence) < 6) ++ { ++ /* input ends unexpectedly */ ++ goto fail; ++ } ++ ++ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) ++ { ++ /* missing second half of the surrogate pair */ ++ goto fail; ++ } ++ ++ /* get the second utf16 sequence */ ++ second_code = parse_hex4(second_sequence + 2); ++ /* check that the code is valid */ ++ if ((second_code < 0xDC00) || (second_code > 0xDFFF)) ++ { ++ /* invalid second half of the surrogate pair */ ++ goto fail; ++ } ++ ++ ++ /* calculate the unicode codepoint from the surrogate pair */ ++ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); ++ } ++ else ++ { ++ sequence_length = 6; /* \uXXXX */ ++ codepoint = first_code; ++ } ++ ++ /* encode as UTF-8 ++ * takes at maximum 4 bytes to encode: ++ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ ++ if (codepoint < 0x80) ++ { ++ /* normal ascii, encoding 0xxxxxxx */ ++ utf8_length = 1; ++ } ++ else if (codepoint < 0x800) ++ { ++ /* two bytes, encoding 110xxxxx 10xxxxxx */ ++ utf8_length = 2; ++ first_byte_mark = 0xC0; /* 11000000 */ ++ } ++ else if (codepoint < 0x10000) ++ { ++ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ ++ utf8_length = 3; ++ first_byte_mark = 0xE0; /* 11100000 */ ++ } ++ else if (codepoint <= 0x10FFFF) ++ { ++ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ ++ utf8_length = 4; ++ first_byte_mark = 0xF0; /* 11110000 */ ++ } ++ else ++ { ++ /* invalid unicode codepoint */ ++ goto fail; ++ } ++ ++ /* encode as utf8 */ ++ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) ++ { ++ /* 10xxxxxx */ ++ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); ++ codepoint >>= 6; ++ } ++ /* encode first byte */ ++ if (utf8_length > 1) ++ { ++ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); ++ } ++ else ++ { ++ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); ++ } ++ ++ *output_pointer += utf8_length; ++ ++ return sequence_length; ++ ++fail: ++ return 0; ++} ++ ++/* Parse the input text into an unescaped cinput, and populate item. */ ++static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) ++{ ++ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; ++ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; ++ unsigned char *output_pointer = NULL; ++ unsigned char *output = NULL; ++ ++ /* not a string */ ++ if (buffer_at_offset(input_buffer)[0] != '\"') ++ { ++ goto fail; ++ } ++ ++ { ++ /* calculate approximate size of the output (overestimate) */ ++ size_t allocation_length = 0; ++ size_t skipped_bytes = 0; ++ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) ++ { ++ /* is escape sequence */ ++ if (input_end[0] == '\\') ++ { ++ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) ++ { ++ /* prevent buffer overflow when last input character is a backslash */ ++ goto fail; ++ } ++ skipped_bytes++; ++ input_end++; ++ } ++ input_end++; ++ } ++ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) ++ { ++ goto fail; /* string ended unexpectedly */ ++ } ++ ++ /* This is at most how much we need for the output */ ++ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; ++ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); ++ if (output == NULL) ++ { ++ goto fail; /* allocation failure */ ++ } ++ } ++ ++ output_pointer = output; ++ /* loop through the string literal */ ++ while (input_pointer < input_end) ++ { ++ if (*input_pointer != '\\') ++ { ++ *output_pointer++ = *input_pointer++; ++ } ++ /* escape sequence */ ++ else ++ { ++ unsigned char sequence_length = 2; ++ if ((input_end - input_pointer) < 1) ++ { ++ goto fail; ++ } ++ ++ switch (input_pointer[1]) ++ { ++ case 'b': ++ *output_pointer++ = '\b'; ++ break; ++ case 'f': ++ *output_pointer++ = '\f'; ++ break; ++ case 'n': ++ *output_pointer++ = '\n'; ++ break; ++ case 'r': ++ *output_pointer++ = '\r'; ++ break; ++ case 't': ++ *output_pointer++ = '\t'; ++ break; ++ case '\"': ++ case '\\': ++ case '/': ++ *output_pointer++ = input_pointer[1]; ++ break; ++ ++ /* UTF-16 literal */ ++ case 'u': ++ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); ++ if (sequence_length == 0) ++ { ++ /* failed to convert UTF16-literal to UTF-8 */ ++ goto fail; ++ } ++ break; ++ ++ default: ++ goto fail; ++ } ++ input_pointer += sequence_length; ++ } ++ } ++ ++ /* zero terminate the output */ ++ *output_pointer = '\0'; ++ ++ item->type = cJSON_String; ++ item->valuestring = (char*)output; ++ ++ input_buffer->offset = (size_t) (input_end - input_buffer->content); ++ input_buffer->offset++; ++ ++ return true; ++ ++fail: ++ if (output != NULL) ++ { ++ input_buffer->hooks.deallocate(output); ++ } ++ ++ if (input_pointer != NULL) ++ { ++ input_buffer->offset = (size_t)(input_pointer - input_buffer->content); ++ } ++ ++ return false; ++} ++ ++/* Render the cstring provided to an escaped version that can be printed. */ ++static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) ++{ ++ const unsigned char *input_pointer = NULL; ++ unsigned char *output = NULL; ++ unsigned char *output_pointer = NULL; ++ size_t output_length = 0; ++ /* numbers of additional characters needed for escaping */ ++ size_t escape_characters = 0; ++ ++ if (output_buffer == NULL) ++ { ++ return false; ++ } ++ ++ /* empty string */ ++ if (input == NULL) ++ { ++ output = ensure(output_buffer, sizeof("\"\"")); ++ if (output == NULL) ++ { ++ return false; ++ } ++ strcpy((char*)output, "\"\""); ++ ++ return true; ++ } ++ ++ /* set "flag" to 1 if something needs to be escaped */ ++ for (input_pointer = input; *input_pointer; input_pointer++) ++ { ++ switch (*input_pointer) ++ { ++ case '\"': ++ case '\\': ++ case '\b': ++ case '\f': ++ case '\n': ++ case '\r': ++ case '\t': ++ /* one character escape sequence */ ++ escape_characters++; ++ break; ++ default: ++ if (*input_pointer < 32) ++ { ++ /* UTF-16 escape sequence uXXXX */ ++ escape_characters += 5; ++ } ++ break; ++ } ++ } ++ output_length = (size_t)(input_pointer - input) + escape_characters; ++ ++ output = ensure(output_buffer, output_length + sizeof("\"\"")); ++ if (output == NULL) ++ { ++ return false; ++ } ++ ++ /* no characters have to be escaped */ ++ if (escape_characters == 0) ++ { ++ output[0] = '\"'; ++ memcpy(output + 1, input, output_length); ++ output[output_length + 1] = '\"'; ++ output[output_length + 2] = '\0'; ++ ++ return true; ++ } ++ ++ output[0] = '\"'; ++ output_pointer = output + 1; ++ /* copy the string */ ++ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) ++ { ++ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) ++ { ++ /* normal character, copy */ ++ *output_pointer = *input_pointer; ++ } ++ else ++ { ++ /* character needs to be escaped */ ++ *output_pointer++ = '\\'; ++ switch (*input_pointer) ++ { ++ case '\\': ++ *output_pointer = '\\'; ++ break; ++ case '\"': ++ *output_pointer = '\"'; ++ break; ++ case '\b': ++ *output_pointer = 'b'; ++ break; ++ case '\f': ++ *output_pointer = 'f'; ++ break; ++ case '\n': ++ *output_pointer = 'n'; ++ break; ++ case '\r': ++ *output_pointer = 'r'; ++ break; ++ case '\t': ++ *output_pointer = 't'; ++ break; ++ default: ++ /* escape and print as unicode codepoint */ ++ sprintf((char*)output_pointer, "u%04x", *input_pointer); ++ output_pointer += 4; ++ break; ++ } ++ } ++ } ++ output[output_length + 1] = '\"'; ++ output[output_length + 2] = '\0'; ++ ++ return true; ++} ++ ++/* Invoke print_string_ptr (which is useful) on an item. */ ++static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) ++{ ++ return print_string_ptr((unsigned char*)item->valuestring, p); ++} ++ ++/* Predeclare these prototypes. */ ++static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); ++static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); ++static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); ++static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); ++static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); ++static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); ++ ++/* Utility to jump whitespace and cr/lf */ ++static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) ++{ ++ if ((buffer == NULL) || (buffer->content == NULL)) ++ { ++ return NULL; ++ } ++ ++ if (cannot_access_at_index(buffer, 0)) ++ { ++ return buffer; ++ } ++ ++ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) ++ { ++ buffer->offset++; ++ } ++ ++ if (buffer->offset == buffer->length) ++ { ++ buffer->offset--; ++ } ++ ++ return buffer; ++} ++ ++/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ ++static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) ++{ ++ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) ++ { ++ return NULL; ++ } ++ ++ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) ++ { ++ buffer->offset += 3; ++ } ++ ++ return buffer; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) ++{ ++ size_t buffer_length; ++ ++ if (NULL == value) ++ { ++ return NULL; ++ } ++ ++ /* Adding null character size due to require_null_terminated. */ ++ buffer_length = strlen(value) + sizeof(""); ++ ++ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); ++} ++ ++/* Parse an object - create a new root, and populate. */ ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) ++{ ++ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; ++ cJSON *item = NULL; ++ ++ /* reset error position */ ++ global_error.json = NULL; ++ global_error.position = 0; ++ ++ if (value == NULL || 0 == buffer_length) ++ { ++ goto fail; ++ } ++ ++ buffer.content = (const unsigned char*)value; ++ buffer.length = buffer_length; ++ buffer.offset = 0; ++ buffer.hooks = global_hooks; ++ ++ item = cJSON_New_Item(&global_hooks); ++ if (item == NULL) /* memory fail */ ++ { ++ goto fail; ++ } ++ ++ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) ++ { ++ /* parse failure. ep is set. */ ++ goto fail; ++ } ++ ++ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ ++ if (require_null_terminated) ++ { ++ buffer_skip_whitespace(&buffer); ++ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') ++ { ++ goto fail; ++ } ++ } ++ if (return_parse_end) ++ { ++ *return_parse_end = (const char*)buffer_at_offset(&buffer); ++ } ++ ++ return item; ++ ++fail: ++ if (item != NULL) ++ { ++ cJSON_Delete(item); ++ } ++ ++ if (value != NULL) ++ { ++ error local_error; ++ local_error.json = (const unsigned char*)value; ++ local_error.position = 0; ++ ++ if (buffer.offset < buffer.length) ++ { ++ local_error.position = buffer.offset; ++ } ++ else if (buffer.length > 0) ++ { ++ local_error.position = buffer.length - 1; ++ } ++ ++ if (return_parse_end != NULL) ++ { ++ *return_parse_end = (const char*)local_error.json + local_error.position; ++ } ++ ++ global_error = local_error; ++ } ++ ++ return NULL; ++} ++ ++/* Default options for cJSON_Parse */ ++CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) ++{ ++ return cJSON_ParseWithOpts(value, 0, 0); ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) ++{ ++ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); ++} ++ ++#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) ++ ++static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) ++{ ++ static const size_t default_buffer_size = 256; ++ printbuffer buffer[1]; ++ unsigned char *printed = NULL; ++ ++ memset(buffer, 0, sizeof(buffer)); ++ ++ /* create buffer */ ++ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); ++ buffer->length = default_buffer_size; ++ buffer->format = format; ++ buffer->hooks = *hooks; ++ if (buffer->buffer == NULL) ++ { ++ goto fail; ++ } ++ ++ /* print the value */ ++ if (!print_value(item, buffer)) ++ { ++ goto fail; ++ } ++ update_offset(buffer); ++ ++ /* check if reallocate is available */ ++ if (hooks->reallocate != NULL) ++ { ++ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); ++ if (printed == NULL) { ++ goto fail; ++ } ++ buffer->buffer = NULL; ++ } ++ else /* otherwise copy the JSON over to a new buffer */ ++ { ++ printed = (unsigned char*) hooks->allocate(buffer->offset + 1); ++ if (printed == NULL) ++ { ++ goto fail; ++ } ++ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); ++ printed[buffer->offset] = '\0'; /* just to be sure */ ++ ++ /* free the buffer */ ++ hooks->deallocate(buffer->buffer); ++ } ++ ++ return printed; ++ ++fail: ++ if (buffer->buffer != NULL) ++ { ++ hooks->deallocate(buffer->buffer); ++ } ++ ++ if (printed != NULL) ++ { ++ hooks->deallocate(printed); ++ } ++ ++ return NULL; ++} ++ ++/* Render a cJSON item/entity/structure to text. */ ++CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) ++{ ++ return (char*)print(item, true, &global_hooks); ++} ++ ++CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) ++{ ++ return (char*)print(item, false, &global_hooks); ++} ++ ++CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) ++{ ++ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; ++ ++ if (prebuffer < 0) ++ { ++ return NULL; ++ } ++ ++ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); ++ if (!p.buffer) ++ { ++ return NULL; ++ } ++ ++ p.length = (size_t)prebuffer; ++ p.offset = 0; ++ p.noalloc = false; ++ p.format = fmt; ++ p.hooks = global_hooks; ++ ++ if (!print_value(item, &p)) ++ { ++ global_hooks.deallocate(p.buffer); ++ return NULL; ++ } ++ ++ return (char*)p.buffer; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) ++{ ++ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; ++ ++ if ((length < 0) || (buffer == NULL)) ++ { ++ return false; ++ } ++ ++ p.buffer = (unsigned char*)buffer; ++ p.length = (size_t)length; ++ p.offset = 0; ++ p.noalloc = true; ++ p.format = format; ++ p.hooks = global_hooks; ++ ++ return print_value(item, &p); ++} ++ ++/* Parser core - when encountering text, process appropriately. */ ++static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) ++{ ++ if ((input_buffer == NULL) || (input_buffer->content == NULL)) ++ { ++ return false; /* no input */ ++ } ++ ++ /* parse the different types of values */ ++ /* null */ ++ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) ++ { ++ item->type = cJSON_NULL; ++ input_buffer->offset += 4; ++ return true; ++ } ++ /* false */ ++ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) ++ { ++ item->type = cJSON_False; ++ input_buffer->offset += 5; ++ return true; ++ } ++ /* true */ ++ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) ++ { ++ item->type = cJSON_True; ++ item->valueint = 1; ++ input_buffer->offset += 4; ++ return true; ++ } ++ /* string */ ++ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) ++ { ++ return parse_string(item, input_buffer); ++ } ++ /* number */ ++ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) ++ { ++ return parse_number(item, input_buffer); ++ } ++ /* array */ ++ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) ++ { ++ return parse_array(item, input_buffer); ++ } ++ /* object */ ++ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) ++ { ++ return parse_object(item, input_buffer); ++ } ++ ++ return false; ++} ++ ++/* Render a value to text. */ ++static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) ++{ ++ unsigned char *output = NULL; ++ ++ if ((item == NULL) || (output_buffer == NULL)) ++ { ++ return false; ++ } ++ ++ switch ((item->type) & 0xFF) ++ { ++ case cJSON_NULL: ++ output = ensure(output_buffer, 5); ++ if (output == NULL) ++ { ++ return false; ++ } ++ strcpy((char*)output, "null"); ++ return true; ++ ++ case cJSON_False: ++ output = ensure(output_buffer, 6); ++ if (output == NULL) ++ { ++ return false; ++ } ++ strcpy((char*)output, "false"); ++ return true; ++ ++ case cJSON_True: ++ output = ensure(output_buffer, 5); ++ if (output == NULL) ++ { ++ return false; ++ } ++ strcpy((char*)output, "true"); ++ return true; ++ ++ case cJSON_Number: ++ return print_number(item, output_buffer); ++ ++ case cJSON_Raw: ++ { ++ size_t raw_length = 0; ++ if (item->valuestring == NULL) ++ { ++ return false; ++ } ++ ++ raw_length = strlen(item->valuestring) + sizeof(""); ++ output = ensure(output_buffer, raw_length); ++ if (output == NULL) ++ { ++ return false; ++ } ++ memcpy(output, item->valuestring, raw_length); ++ return true; ++ } ++ ++ case cJSON_String: ++ return print_string(item, output_buffer); ++ ++ case cJSON_Array: ++ return print_array(item, output_buffer); ++ ++ case cJSON_Object: ++ return print_object(item, output_buffer); ++ ++ default: ++ return false; ++ } ++} ++ ++/* Build an array from input text. */ ++static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) ++{ ++ cJSON *head = NULL; /* head of the linked list */ ++ cJSON *current_item = NULL; ++ ++ if (input_buffer->depth >= CJSON_NESTING_LIMIT) ++ { ++ return false; /* to deeply nested */ ++ } ++ input_buffer->depth++; ++ ++ if (buffer_at_offset(input_buffer)[0] != '[') ++ { ++ /* not an array */ ++ goto fail; ++ } ++ ++ input_buffer->offset++; ++ buffer_skip_whitespace(input_buffer); ++ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) ++ { ++ /* empty array */ ++ goto success; ++ } ++ ++ /* check if we skipped to the end of the buffer */ ++ if (cannot_access_at_index(input_buffer, 0)) ++ { ++ input_buffer->offset--; ++ goto fail; ++ } ++ ++ /* step back to character in front of the first element */ ++ input_buffer->offset--; ++ /* loop through the comma separated array elements */ ++ do ++ { ++ /* allocate next item */ ++ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); ++ if (new_item == NULL) ++ { ++ goto fail; /* allocation failure */ ++ } ++ ++ /* attach next item to list */ ++ if (head == NULL) ++ { ++ /* start the linked list */ ++ current_item = head = new_item; ++ } ++ else ++ { ++ /* add to the end and advance */ ++ current_item->next = new_item; ++ new_item->prev = current_item; ++ current_item = new_item; ++ } ++ ++ /* parse next value */ ++ input_buffer->offset++; ++ buffer_skip_whitespace(input_buffer); ++ if (!parse_value(current_item, input_buffer)) ++ { ++ goto fail; /* failed to parse value */ ++ } ++ buffer_skip_whitespace(input_buffer); ++ } ++ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); ++ ++ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') ++ { ++ goto fail; /* expected end of array */ ++ } ++ ++success: ++ input_buffer->depth--; ++ ++ if (head != NULL) { ++ head->prev = current_item; ++ } ++ ++ item->type = cJSON_Array; ++ item->child = head; ++ ++ input_buffer->offset++; ++ ++ return true; ++ ++fail: ++ if (head != NULL) ++ { ++ cJSON_Delete(head); ++ } ++ ++ return false; ++} ++ ++/* Render an array to text */ ++static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) ++{ ++ unsigned char *output_pointer = NULL; ++ size_t length = 0; ++ cJSON *current_element = item->child; ++ ++ if (output_buffer == NULL) ++ { ++ return false; ++ } ++ ++ /* Compose the output array. */ ++ /* opening square bracket */ ++ output_pointer = ensure(output_buffer, 1); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ ++ *output_pointer = '['; ++ output_buffer->offset++; ++ output_buffer->depth++; ++ ++ while (current_element != NULL) ++ { ++ if (!print_value(current_element, output_buffer)) ++ { ++ return false; ++ } ++ update_offset(output_buffer); ++ if (current_element->next) ++ { ++ length = (size_t) (output_buffer->format ? 2 : 1); ++ output_pointer = ensure(output_buffer, length + 1); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ *output_pointer++ = ','; ++ if(output_buffer->format) ++ { ++ *output_pointer++ = ' '; ++ } ++ *output_pointer = '\0'; ++ output_buffer->offset += length; ++ } ++ current_element = current_element->next; ++ } ++ ++ output_pointer = ensure(output_buffer, 2); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ *output_pointer++ = ']'; ++ *output_pointer = '\0'; ++ output_buffer->depth--; ++ ++ return true; ++} ++ ++/* Build an object from the text. */ ++static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) ++{ ++ cJSON *head = NULL; /* linked list head */ ++ cJSON *current_item = NULL; ++ ++ if (input_buffer->depth >= CJSON_NESTING_LIMIT) ++ { ++ return false; /* to deeply nested */ ++ } ++ input_buffer->depth++; ++ ++ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) ++ { ++ goto fail; /* not an object */ ++ } ++ ++ input_buffer->offset++; ++ buffer_skip_whitespace(input_buffer); ++ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) ++ { ++ goto success; /* empty object */ ++ } ++ ++ /* check if we skipped to the end of the buffer */ ++ if (cannot_access_at_index(input_buffer, 0)) ++ { ++ input_buffer->offset--; ++ goto fail; ++ } ++ ++ /* step back to character in front of the first element */ ++ input_buffer->offset--; ++ /* loop through the comma separated array elements */ ++ do ++ { ++ /* allocate next item */ ++ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); ++ if (new_item == NULL) ++ { ++ goto fail; /* allocation failure */ ++ } ++ ++ /* attach next item to list */ ++ if (head == NULL) ++ { ++ /* start the linked list */ ++ current_item = head = new_item; ++ } ++ else ++ { ++ /* add to the end and advance */ ++ current_item->next = new_item; ++ new_item->prev = current_item; ++ current_item = new_item; ++ } ++ ++ /* parse the name of the child */ ++ input_buffer->offset++; ++ buffer_skip_whitespace(input_buffer); ++ if (!parse_string(current_item, input_buffer)) ++ { ++ goto fail; /* failed to parse name */ ++ } ++ buffer_skip_whitespace(input_buffer); ++ ++ /* swap valuestring and string, because we parsed the name */ ++ current_item->string = current_item->valuestring; ++ current_item->valuestring = NULL; ++ ++ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) ++ { ++ goto fail; /* invalid object */ ++ } ++ ++ /* parse the value */ ++ input_buffer->offset++; ++ buffer_skip_whitespace(input_buffer); ++ if (!parse_value(current_item, input_buffer)) ++ { ++ goto fail; /* failed to parse value */ ++ } ++ buffer_skip_whitespace(input_buffer); ++ } ++ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); ++ ++ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) ++ { ++ goto fail; /* expected end of object */ ++ } ++ ++success: ++ input_buffer->depth--; ++ ++ if (head != NULL) { ++ head->prev = current_item; ++ } ++ ++ item->type = cJSON_Object; ++ item->child = head; ++ ++ input_buffer->offset++; ++ return true; ++ ++fail: ++ if (head != NULL) ++ { ++ cJSON_Delete(head); ++ } ++ ++ return false; ++} ++ ++/* Render an object to text. */ ++static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) ++{ ++ unsigned char *output_pointer = NULL; ++ size_t length = 0; ++ cJSON *current_item = item->child; ++ ++ if (output_buffer == NULL) ++ { ++ return false; ++ } ++ ++ /* Compose the output: */ ++ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ ++ output_pointer = ensure(output_buffer, length + 1); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ ++ *output_pointer++ = '{'; ++ output_buffer->depth++; ++ if (output_buffer->format) ++ { ++ *output_pointer++ = '\n'; ++ } ++ output_buffer->offset += length; ++ ++ while (current_item) ++ { ++ if (output_buffer->format) ++ { ++ size_t i; ++ output_pointer = ensure(output_buffer, output_buffer->depth); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ for (i = 0; i < output_buffer->depth; i++) ++ { ++ *output_pointer++ = '\t'; ++ } ++ output_buffer->offset += output_buffer->depth; ++ } ++ ++ /* print key */ ++ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) ++ { ++ return false; ++ } ++ update_offset(output_buffer); ++ ++ length = (size_t) (output_buffer->format ? 2 : 1); ++ output_pointer = ensure(output_buffer, length); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ *output_pointer++ = ':'; ++ if (output_buffer->format) ++ { ++ *output_pointer++ = '\t'; ++ } ++ output_buffer->offset += length; ++ ++ /* print value */ ++ if (!print_value(current_item, output_buffer)) ++ { ++ return false; ++ } ++ update_offset(output_buffer); ++ ++ /* print comma if not last */ ++ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); ++ output_pointer = ensure(output_buffer, length + 1); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ if (current_item->next) ++ { ++ *output_pointer++ = ','; ++ } ++ ++ if (output_buffer->format) ++ { ++ *output_pointer++ = '\n'; ++ } ++ *output_pointer = '\0'; ++ output_buffer->offset += length; ++ ++ current_item = current_item->next; ++ } ++ ++ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); ++ if (output_pointer == NULL) ++ { ++ return false; ++ } ++ if (output_buffer->format) ++ { ++ size_t i; ++ for (i = 0; i < (output_buffer->depth - 1); i++) ++ { ++ *output_pointer++ = '\t'; ++ } ++ } ++ *output_pointer++ = '}'; ++ *output_pointer = '\0'; ++ output_buffer->depth--; ++ ++ return true; ++} ++ ++/* Get Array size/item / object item. */ ++CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) ++{ ++ cJSON *child = NULL; ++ size_t size = 0; ++ ++ if (array == NULL) ++ { ++ return 0; ++ } ++ ++ child = array->child; ++ ++ while(child != NULL) ++ { ++ size++; ++ child = child->next; ++ } ++ ++ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ ++ ++ return (int)size; ++} ++ ++static cJSON* get_array_item(const cJSON *array, size_t index) ++{ ++ cJSON *current_child = NULL; ++ ++ if (array == NULL) ++ { ++ return NULL; ++ } ++ ++ current_child = array->child; ++ while ((current_child != NULL) && (index > 0)) ++ { ++ index--; ++ current_child = current_child->next; ++ } ++ ++ return current_child; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) ++{ ++ if (index < 0) ++ { ++ return NULL; ++ } ++ ++ return get_array_item(array, (size_t)index); ++} ++ ++static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) ++{ ++ cJSON *current_element = NULL; ++ ++ if ((object == NULL) || (name == NULL)) ++ { ++ return NULL; ++ } ++ ++ current_element = object->child; ++ if (case_sensitive) ++ { ++ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) ++ { ++ current_element = current_element->next; ++ } ++ } ++ else ++ { ++ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) ++ { ++ current_element = current_element->next; ++ } ++ } ++ ++ if ((current_element == NULL) || (current_element->string == NULL)) { ++ return NULL; ++ } ++ ++ return current_element; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) ++{ ++ return get_object_item(object, string, false); ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) ++{ ++ return get_object_item(object, string, true); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) ++{ ++ return cJSON_GetObjectItem(object, string) ? 1 : 0; ++} ++ ++/* Utility for array list handling. */ ++static void suffix_object(cJSON *prev, cJSON *item) ++{ ++ prev->next = item; ++ item->prev = prev; ++} ++ ++/* Utility for handling references. */ ++static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) ++{ ++ cJSON *reference = NULL; ++ if (item == NULL) ++ { ++ return NULL; ++ } ++ ++ reference = cJSON_New_Item(hooks); ++ if (reference == NULL) ++ { ++ return NULL; ++ } ++ ++ memcpy(reference, item, sizeof(cJSON)); ++ reference->string = NULL; ++ reference->type |= cJSON_IsReference; ++ reference->next = reference->prev = NULL; ++ return reference; ++} ++ ++static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) ++{ ++ cJSON *child = NULL; ++ ++ if ((item == NULL) || (array == NULL) || (array == item)) ++ { ++ return false; ++ } ++ ++ child = array->child; ++ /* ++ * To find the last item in array quickly, we use prev in array ++ */ ++ if (child == NULL) ++ { ++ /* list is empty, start new one */ ++ array->child = item; ++ item->prev = item; ++ item->next = NULL; ++ } ++ else ++ { ++ /* append to the end */ ++ if (child->prev) ++ { ++ suffix_object(child->prev, item); ++ array->child->prev = item; ++ } ++ } ++ ++ return true; ++} ++ ++/* Add item to array/object. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) ++{ ++ return add_item_to_array(array, item); ++} ++ ++#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) ++ #pragma GCC diagnostic push ++#endif ++#ifdef __GNUC__ ++#pragma GCC diagnostic ignored "-Wcast-qual" ++#endif ++/* helper function to cast away const */ ++static void* cast_away_const(const void* string) ++{ ++ return (void*)string; ++} ++#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) ++ #pragma GCC diagnostic pop ++#endif ++ ++ ++static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) ++{ ++ char *new_key = NULL; ++ int new_type = cJSON_Invalid; ++ ++ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) ++ { ++ return false; ++ } ++ ++ if (constant_key) ++ { ++ new_key = (char*)cast_away_const(string); ++ new_type = item->type | cJSON_StringIsConst; ++ } ++ else ++ { ++ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); ++ if (new_key == NULL) ++ { ++ return false; ++ } ++ ++ new_type = item->type & ~cJSON_StringIsConst; ++ } ++ ++ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) ++ { ++ hooks->deallocate(item->string); ++ } ++ ++ item->string = new_key; ++ item->type = new_type; ++ ++ return add_item_to_array(object, item); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) ++{ ++ return add_item_to_object(object, string, item, &global_hooks, false); ++} ++ ++/* Add an item to an object with constant string as key */ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) ++{ ++ return add_item_to_object(object, string, item, &global_hooks, true); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) ++{ ++ if (array == NULL) ++ { ++ return false; ++ } ++ ++ return add_item_to_array(array, create_reference(item, &global_hooks)); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) ++{ ++ if ((object == NULL) || (string == NULL)) ++ { ++ return false; ++ } ++ ++ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) ++{ ++ cJSON *null = cJSON_CreateNull(); ++ if (add_item_to_object(object, name, null, &global_hooks, false)) ++ { ++ return null; ++ } ++ ++ cJSON_Delete(null); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) ++{ ++ cJSON *true_item = cJSON_CreateTrue(); ++ if (add_item_to_object(object, name, true_item, &global_hooks, false)) ++ { ++ return true_item; ++ } ++ ++ cJSON_Delete(true_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) ++{ ++ cJSON *false_item = cJSON_CreateFalse(); ++ if (add_item_to_object(object, name, false_item, &global_hooks, false)) ++ { ++ return false_item; ++ } ++ ++ cJSON_Delete(false_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) ++{ ++ cJSON *bool_item = cJSON_CreateBool(boolean); ++ if (add_item_to_object(object, name, bool_item, &global_hooks, false)) ++ { ++ return bool_item; ++ } ++ ++ cJSON_Delete(bool_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) ++{ ++ cJSON *number_item = cJSON_CreateNumber(number); ++ if (add_item_to_object(object, name, number_item, &global_hooks, false)) ++ { ++ return number_item; ++ } ++ ++ cJSON_Delete(number_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) ++{ ++ cJSON *string_item = cJSON_CreateString(string); ++ if (add_item_to_object(object, name, string_item, &global_hooks, false)) ++ { ++ return string_item; ++ } ++ ++ cJSON_Delete(string_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) ++{ ++ cJSON *raw_item = cJSON_CreateRaw(raw); ++ if (add_item_to_object(object, name, raw_item, &global_hooks, false)) ++ { ++ return raw_item; ++ } ++ ++ cJSON_Delete(raw_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) ++{ ++ cJSON *object_item = cJSON_CreateObject(); ++ if (add_item_to_object(object, name, object_item, &global_hooks, false)) ++ { ++ return object_item; ++ } ++ ++ cJSON_Delete(object_item); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) ++{ ++ cJSON *array = cJSON_CreateArray(); ++ if (add_item_to_object(object, name, array, &global_hooks, false)) ++ { ++ return array; ++ } ++ ++ cJSON_Delete(array); ++ return NULL; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) ++{ ++ if ((parent == NULL) || (item == NULL)) ++ { ++ return NULL; ++ } ++ ++ if (item != parent->child) ++ { ++ /* not the first element */ ++ item->prev->next = item->next; ++ } ++ if (item->next != NULL) ++ { ++ /* not the last element */ ++ item->next->prev = item->prev; ++ } ++ ++ if (item == parent->child) ++ { ++ /* first element */ ++ parent->child = item->next; ++ } ++ else if (item->next == NULL) ++ { ++ /* last element */ ++ parent->child->prev = item->prev; ++ } ++ ++ /* make sure the detached item doesn't point anywhere anymore */ ++ item->prev = NULL; ++ item->next = NULL; ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) ++{ ++ if (which < 0) ++ { ++ return NULL; ++ } ++ ++ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); ++} ++ ++CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) ++{ ++ cJSON_Delete(cJSON_DetachItemFromArray(array, which)); ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) ++{ ++ cJSON *to_detach = cJSON_GetObjectItem(object, string); ++ ++ return cJSON_DetachItemViaPointer(object, to_detach); ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) ++{ ++ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); ++ ++ return cJSON_DetachItemViaPointer(object, to_detach); ++} ++ ++CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) ++{ ++ cJSON_Delete(cJSON_DetachItemFromObject(object, string)); ++} ++ ++CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) ++{ ++ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); ++} ++ ++/* Replace array/object items with new ones. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) ++{ ++ cJSON *after_inserted = NULL; ++ ++ if (which < 0) ++ { ++ return false; ++ } ++ ++ after_inserted = get_array_item(array, (size_t)which); ++ if (after_inserted == NULL) ++ { ++ return add_item_to_array(array, newitem); ++ } ++ ++ newitem->next = after_inserted; ++ newitem->prev = after_inserted->prev; ++ after_inserted->prev = newitem; ++ if (after_inserted == array->child) ++ { ++ array->child = newitem; ++ } ++ else ++ { ++ newitem->prev->next = newitem; ++ } ++ return true; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) ++{ ++ if ((parent == NULL) || (replacement == NULL) || (item == NULL)) ++ { ++ return false; ++ } ++ ++ if (replacement == item) ++ { ++ return true; ++ } ++ ++ replacement->next = item->next; ++ replacement->prev = item->prev; ++ ++ if (replacement->next != NULL) ++ { ++ replacement->next->prev = replacement; ++ } ++ if (parent->child == item) ++ { ++ if (parent->child->prev == parent->child) ++ { ++ replacement->prev = replacement; ++ } ++ parent->child = replacement; ++ } ++ else ++ { /* ++ * To find the last item in array quickly, we use prev in array. ++ * We can't modify the last item's next pointer where this item was the parent's child ++ */ ++ if (replacement->prev != NULL) ++ { ++ replacement->prev->next = replacement; ++ } ++ if (replacement->next == NULL) ++ { ++ parent->child->prev = replacement; ++ } ++ } ++ ++ item->next = NULL; ++ item->prev = NULL; ++ cJSON_Delete(item); ++ ++ return true; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) ++{ ++ if (which < 0) ++ { ++ return false; ++ } ++ ++ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); ++} ++ ++static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) ++{ ++ if ((replacement == NULL) || (string == NULL)) ++ { ++ return false; ++ } ++ ++ /* replace the name in the replacement */ ++ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) ++ { ++ cJSON_free(replacement->string); ++ } ++ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); ++ if (replacement->string == NULL) ++ { ++ return false; ++ } ++ ++ replacement->type &= ~cJSON_StringIsConst; ++ ++ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) ++{ ++ return replace_item_in_object(object, string, newitem, false); ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) ++{ ++ return replace_item_in_object(object, string, newitem, true); ++} ++ ++/* Create basic types: */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_NULL; ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_True; ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_False; ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = boolean ? cJSON_True : cJSON_False; ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_Number; ++ item->valuedouble = num; ++ ++ /* use saturation in case of overflow */ ++ if (num >= INT_MAX) ++ { ++ item->valueint = INT_MAX; ++ } ++ else if (num <= (double)INT_MIN) ++ { ++ item->valueint = INT_MIN; ++ } ++ else ++ { ++ item->valueint = (int)num; ++ } ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_String; ++ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); ++ if(!item->valuestring) ++ { ++ cJSON_Delete(item); ++ return NULL; ++ } ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if (item != NULL) ++ { ++ item->type = cJSON_String | cJSON_IsReference; ++ item->valuestring = (char*)cast_away_const(string); ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if (item != NULL) { ++ item->type = cJSON_Object | cJSON_IsReference; ++ item->child = (cJSON*)cast_away_const(child); ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if (item != NULL) { ++ item->type = cJSON_Array | cJSON_IsReference; ++ item->child = (cJSON*)cast_away_const(child); ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type = cJSON_Raw; ++ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); ++ if(!item->valuestring) ++ { ++ cJSON_Delete(item); ++ return NULL; ++ } ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if(item) ++ { ++ item->type=cJSON_Array; ++ } ++ ++ return item; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) ++{ ++ cJSON *item = cJSON_New_Item(&global_hooks); ++ if (item) ++ { ++ item->type = cJSON_Object; ++ } ++ ++ return item; ++} ++ ++/* Create Arrays: */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) ++{ ++ size_t i = 0; ++ cJSON *n = NULL; ++ cJSON *p = NULL; ++ cJSON *a = NULL; ++ ++ if ((count < 0) || (numbers == NULL)) ++ { ++ return NULL; ++ } ++ ++ a = cJSON_CreateArray(); ++ ++ for(i = 0; a && (i < (size_t)count); i++) ++ { ++ n = cJSON_CreateNumber(numbers[i]); ++ if (!n) ++ { ++ cJSON_Delete(a); ++ return NULL; ++ } ++ if(!i) ++ { ++ a->child = n; ++ } ++ else ++ { ++ suffix_object(p, n); ++ } ++ p = n; ++ } ++ ++ if (a && a->child) { ++ a->child->prev = n; ++ } ++ ++ return a; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) ++{ ++ size_t i = 0; ++ cJSON *n = NULL; ++ cJSON *p = NULL; ++ cJSON *a = NULL; ++ ++ if ((count < 0) || (numbers == NULL)) ++ { ++ return NULL; ++ } ++ ++ a = cJSON_CreateArray(); ++ ++ for(i = 0; a && (i < (size_t)count); i++) ++ { ++ n = cJSON_CreateNumber((double)numbers[i]); ++ if(!n) ++ { ++ cJSON_Delete(a); ++ return NULL; ++ } ++ if(!i) ++ { ++ a->child = n; ++ } ++ else ++ { ++ suffix_object(p, n); ++ } ++ p = n; ++ } ++ ++ if (a && a->child) { ++ a->child->prev = n; ++ } ++ ++ return a; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) ++{ ++ size_t i = 0; ++ cJSON *n = NULL; ++ cJSON *p = NULL; ++ cJSON *a = NULL; ++ ++ if ((count < 0) || (numbers == NULL)) ++ { ++ return NULL; ++ } ++ ++ a = cJSON_CreateArray(); ++ ++ for(i = 0; a && (i < (size_t)count); i++) ++ { ++ n = cJSON_CreateNumber(numbers[i]); ++ if(!n) ++ { ++ cJSON_Delete(a); ++ return NULL; ++ } ++ if(!i) ++ { ++ a->child = n; ++ } ++ else ++ { ++ suffix_object(p, n); ++ } ++ p = n; ++ } ++ ++ if (a && a->child) { ++ a->child->prev = n; ++ } ++ ++ return a; ++} ++ ++CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) ++{ ++ size_t i = 0; ++ cJSON *n = NULL; ++ cJSON *p = NULL; ++ cJSON *a = NULL; ++ ++ if ((count < 0) || (strings == NULL)) ++ { ++ return NULL; ++ } ++ ++ a = cJSON_CreateArray(); ++ ++ for (i = 0; a && (i < (size_t)count); i++) ++ { ++ n = cJSON_CreateString(strings[i]); ++ if(!n) ++ { ++ cJSON_Delete(a); ++ return NULL; ++ } ++ if(!i) ++ { ++ a->child = n; ++ } ++ else ++ { ++ suffix_object(p,n); ++ } ++ p = n; ++ } ++ ++ if (a && a->child) { ++ a->child->prev = n; ++ } ++ ++ return a; ++} ++ ++/* Duplication */ ++CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) ++{ ++ cJSON *newitem = NULL; ++ cJSON *child = NULL; ++ cJSON *next = NULL; ++ cJSON *newchild = NULL; ++ ++ /* Bail on bad ptr */ ++ if (!item) ++ { ++ goto fail; ++ } ++ /* Create new item */ ++ newitem = cJSON_New_Item(&global_hooks); ++ if (!newitem) ++ { ++ goto fail; ++ } ++ /* Copy over all vars */ ++ newitem->type = item->type & (~cJSON_IsReference); ++ newitem->valueint = item->valueint; ++ newitem->valuedouble = item->valuedouble; ++ if (item->valuestring) ++ { ++ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); ++ if (!newitem->valuestring) ++ { ++ goto fail; ++ } ++ } ++ if (item->string) ++ { ++ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); ++ if (!newitem->string) ++ { ++ goto fail; ++ } ++ } ++ /* If non-recursive, then we're done! */ ++ if (!recurse) ++ { ++ return newitem; ++ } ++ /* Walk the ->next chain for the child. */ ++ child = item->child; ++ while (child != NULL) ++ { ++ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ ++ if (!newchild) ++ { ++ goto fail; ++ } ++ if (next != NULL) ++ { ++ /* If newitem->child already set, then crosswire ->prev and ->next and move on */ ++ next->next = newchild; ++ newchild->prev = next; ++ next = newchild; ++ } ++ else ++ { ++ /* Set newitem->child and move to it */ ++ newitem->child = newchild; ++ next = newchild; ++ } ++ child = child->next; ++ } ++ if (newitem && newitem->child) ++ { ++ newitem->child->prev = newchild; ++ } ++ ++ return newitem; ++ ++fail: ++ if (newitem != NULL) ++ { ++ cJSON_Delete(newitem); ++ } ++ ++ return NULL; ++} ++ ++static void skip_oneline_comment(char **input) ++{ ++ *input += static_strlen("//"); ++ ++ for (; (*input)[0] != '\0'; ++(*input)) ++ { ++ if ((*input)[0] == '\n') { ++ *input += static_strlen("\n"); ++ return; ++ } ++ } ++} ++ ++static void skip_multiline_comment(char **input) ++{ ++ *input += static_strlen("/*"); ++ ++ for (; (*input)[0] != '\0'; ++(*input)) ++ { ++ if (((*input)[0] == '*') && ((*input)[1] == '/')) ++ { ++ *input += static_strlen("*/"); ++ return; ++ } ++ } ++} ++ ++static void minify_string(char **input, char **output) { ++ (*output)[0] = (*input)[0]; ++ *input += static_strlen("\""); ++ *output += static_strlen("\""); ++ ++ ++ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { ++ (*output)[0] = (*input)[0]; ++ ++ if ((*input)[0] == '\"') { ++ (*output)[0] = '\"'; ++ *input += static_strlen("\""); ++ *output += static_strlen("\""); ++ return; ++ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { ++ (*output)[1] = (*input)[1]; ++ *input += static_strlen("\""); ++ *output += static_strlen("\""); ++ } ++ } ++} ++ ++CJSON_PUBLIC(void) cJSON_Minify(char *json) ++{ ++ char *into = json; ++ ++ if (json == NULL) ++ { ++ return; ++ } ++ ++ while (json[0] != '\0') ++ { ++ switch (json[0]) ++ { ++ case ' ': ++ case '\t': ++ case '\r': ++ case '\n': ++ json++; ++ break; ++ ++ case '/': ++ if (json[1] == '/') ++ { ++ skip_oneline_comment(&json); ++ } ++ else if (json[1] == '*') ++ { ++ skip_multiline_comment(&json); ++ } else { ++ json++; ++ } ++ break; ++ ++ case '\"': ++ minify_string(&json, (char**)&into); ++ break; ++ ++ default: ++ into[0] = json[0]; ++ json++; ++ into++; ++ } ++ } ++ ++ /* and null-terminate. */ ++ *into = '\0'; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_Invalid; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_False; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xff) == cJSON_True; ++} ++ ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & (cJSON_True | cJSON_False)) != 0; ++} ++CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_NULL; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_Number; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_String; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_Array; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_Object; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) ++{ ++ if (item == NULL) ++ { ++ return false; ++ } ++ ++ return (item->type & 0xFF) == cJSON_Raw; ++} ++ ++CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) ++{ ++ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) ++ { ++ return false; ++ } ++ ++ /* check if type is valid */ ++ switch (a->type & 0xFF) ++ { ++ case cJSON_False: ++ case cJSON_True: ++ case cJSON_NULL: ++ case cJSON_Number: ++ case cJSON_String: ++ case cJSON_Raw: ++ case cJSON_Array: ++ case cJSON_Object: ++ break; ++ ++ default: ++ return false; ++ } ++ ++ /* identical objects are equal */ ++ if (a == b) ++ { ++ return true; ++ } ++ ++ switch (a->type & 0xFF) ++ { ++ /* in these cases and equal type is enough */ ++ case cJSON_False: ++ case cJSON_True: ++ case cJSON_NULL: ++ return true; ++ ++ case cJSON_Number: ++ if (compare_double(a->valuedouble, b->valuedouble)) ++ { ++ return true; ++ } ++ return false; ++ ++ case cJSON_String: ++ case cJSON_Raw: ++ if ((a->valuestring == NULL) || (b->valuestring == NULL)) ++ { ++ return false; ++ } ++ if (strcmp(a->valuestring, b->valuestring) == 0) ++ { ++ return true; ++ } ++ ++ return false; ++ ++ case cJSON_Array: ++ { ++ cJSON *a_element = a->child; ++ cJSON *b_element = b->child; ++ ++ for (; (a_element != NULL) && (b_element != NULL);) ++ { ++ if (!cJSON_Compare(a_element, b_element, case_sensitive)) ++ { ++ return false; ++ } ++ ++ a_element = a_element->next; ++ b_element = b_element->next; ++ } ++ ++ /* one of the arrays is longer than the other */ ++ if (a_element != b_element) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ case cJSON_Object: ++ { ++ cJSON *a_element = NULL; ++ cJSON *b_element = NULL; ++ cJSON_ArrayForEach(a_element, a) ++ { ++ /* TODO This has O(n^2) runtime, which is horrible! */ ++ b_element = get_object_item(b, a_element->string, case_sensitive); ++ if (b_element == NULL) ++ { ++ return false; ++ } ++ ++ if (!cJSON_Compare(a_element, b_element, case_sensitive)) ++ { ++ return false; ++ } ++ } ++ ++ /* doing this twice, once on a and b to prevent true comparison if a subset of b ++ * TODO: Do this the proper way, this is just a fix for now */ ++ cJSON_ArrayForEach(b_element, b) ++ { ++ a_element = get_object_item(a, b_element->string, case_sensitive); ++ if (a_element == NULL) ++ { ++ return false; ++ } ++ ++ if (!cJSON_Compare(b_element, a_element, case_sensitive)) ++ { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ default: ++ return false; ++ } ++} ++ ++CJSON_PUBLIC(void *) cJSON_malloc(size_t size) ++{ ++ return global_hooks.allocate(size); ++} ++ ++CJSON_PUBLIC(void) cJSON_free(void *object) ++{ ++ global_hooks.deallocate(object); ++} +diff --git a/qtfs/rexec/rshim/cJSON.h b/qtfs/rexec/rshim/cJSON.h +new file mode 100644 +index 0000000..95a9cf6 +--- /dev/null ++++ b/qtfs/rexec/rshim/cJSON.h +@@ -0,0 +1,300 @@ ++/* ++ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors ++ ++ Permission is hereby granted, free of charge, to any person obtaining a copy ++ of this software and associated documentation files (the "Software"), to deal ++ in the Software without restriction, including without limitation the rights ++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ copies of the Software, and to permit persons to whom the Software is ++ furnished to do so, subject to the following conditions: ++ ++ The above copyright notice and this permission notice shall be included in ++ all copies or substantial portions of the Software. ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ THE SOFTWARE. ++*/ ++ ++#ifndef cJSON__h ++#define cJSON__h ++ ++#ifdef __cplusplus ++extern "C" ++{ ++#endif ++ ++#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) ++#define __WINDOWS__ ++#endif ++ ++#ifdef __WINDOWS__ ++ ++/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: ++ ++CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols ++CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) ++CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol ++ ++For *nix builds that support visibility attribute, you can define similar behavior by ++ ++setting default visibility to hidden by adding ++-fvisibility=hidden (for gcc) ++or ++-xldscope=hidden (for sun cc) ++to CFLAGS ++ ++then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does ++ ++*/ ++ ++#define CJSON_CDECL __cdecl ++#define CJSON_STDCALL __stdcall ++ ++/* export symbols by default, this is necessary for copy pasting the C and header file */ ++#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) ++#define CJSON_EXPORT_SYMBOLS ++#endif ++ ++#if defined(CJSON_HIDE_SYMBOLS) ++#define CJSON_PUBLIC(type) type CJSON_STDCALL ++#elif defined(CJSON_EXPORT_SYMBOLS) ++#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL ++#elif defined(CJSON_IMPORT_SYMBOLS) ++#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL ++#endif ++#else /* !__WINDOWS__ */ ++#define CJSON_CDECL ++#define CJSON_STDCALL ++ ++#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) ++#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type ++#else ++#define CJSON_PUBLIC(type) type ++#endif ++#endif ++ ++/* project version */ ++#define CJSON_VERSION_MAJOR 1 ++#define CJSON_VERSION_MINOR 7 ++#define CJSON_VERSION_PATCH 15 ++ ++#include ++ ++/* cJSON Types: */ ++#define cJSON_Invalid (0) ++#define cJSON_False (1 << 0) ++#define cJSON_True (1 << 1) ++#define cJSON_NULL (1 << 2) ++#define cJSON_Number (1 << 3) ++#define cJSON_String (1 << 4) ++#define cJSON_Array (1 << 5) ++#define cJSON_Object (1 << 6) ++#define cJSON_Raw (1 << 7) /* raw json */ ++ ++#define cJSON_IsReference 256 ++#define cJSON_StringIsConst 512 ++ ++/* The cJSON structure: */ ++typedef struct cJSON ++{ ++ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ ++ struct cJSON *next; ++ struct cJSON *prev; ++ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ ++ struct cJSON *child; ++ ++ /* The type of the item, as above. */ ++ int type; ++ ++ /* The item's string, if type==cJSON_String and type == cJSON_Raw */ ++ char *valuestring; ++ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ ++ int valueint; ++ /* The item's number, if type==cJSON_Number */ ++ double valuedouble; ++ ++ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ ++ char *string; ++} cJSON; ++ ++typedef struct cJSON_Hooks ++{ ++ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ ++ void *(CJSON_CDECL *malloc_fn)(size_t sz); ++ void (CJSON_CDECL *free_fn)(void *ptr); ++} cJSON_Hooks; ++ ++typedef int cJSON_bool; ++ ++/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. ++ * This is to prevent stack overflows. */ ++#ifndef CJSON_NESTING_LIMIT ++#define CJSON_NESTING_LIMIT 1000 ++#endif ++ ++/* returns the version of cJSON as a string */ ++CJSON_PUBLIC(const char*) cJSON_Version(void); ++ ++/* Supply malloc, realloc and free functions to cJSON */ ++CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); ++ ++/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ ++/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ ++CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); ++/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ ++/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); ++CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); ++ ++/* Render a cJSON entity to text for transfer/storage. */ ++CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); ++/* Render a cJSON entity to text for transfer/storage without any formatting. */ ++CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); ++/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ ++CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); ++/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ ++/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ ++CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); ++/* Delete a cJSON entity and all subentities. */ ++CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); ++ ++/* Returns the number of items in an array (or object). */ ++CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); ++/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ ++CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); ++/* Get item "string" from object. Case insensitive. */ ++CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); ++CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); ++CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); ++/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ ++CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); ++ ++/* Check item type and return its value */ ++CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); ++CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); ++ ++/* These functions check the type of an item */ ++CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); ++CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); ++ ++/* These calls create a cJSON item of the appropriate type. */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); ++CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); ++CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); ++CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); ++CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); ++CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); ++/* raw json */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); ++CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); ++CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); ++ ++/* Create a string where valuestring references a string so ++ * it will not be freed by cJSON_Delete */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); ++/* Create an object/array that only references it's elements so ++ * they will not be freed by cJSON_Delete */ ++CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); ++CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); ++ ++/* These utilities create an Array of count items. ++ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ ++CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); ++CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); ++CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); ++CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); ++ ++/* Append item to the specified array/object. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); ++/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. ++ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before ++ * writing to `item->string` */ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); ++/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); ++CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); ++ ++/* Remove/Detach items from Arrays/Objects. */ ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); ++CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); ++CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); ++CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); ++CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); ++ ++/* Update array items. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); ++CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); ++ ++/* Duplicate a cJSON item */ ++CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); ++/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will ++ * need to be released. With recurse!=0, it will duplicate any children connected to the item. ++ * The item->next and ->prev pointers are always zero on return from Duplicate. */ ++/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. ++ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ ++CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); ++ ++/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. ++ * The input pointer json cannot point to a read-only address area, such as a string constant, ++ * but should point to a readable and writable address area. */ ++CJSON_PUBLIC(void) cJSON_Minify(char *json); ++ ++/* Helper functions for creating and adding items to an object at the same time. ++ * They return the added item or NULL on failure. */ ++CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); ++CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); ++CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); ++CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); ++CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); ++CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); ++CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); ++CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); ++CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); ++ ++/* When assigning an integer value, it needs to be propagated to valuedouble too. */ ++#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) ++/* helper for the cJSON_SetNumberValue macro */ ++CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); ++#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) ++/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ ++CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); ++ ++/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ ++#define cJSON_SetBoolValue(object, boolValue) ( \ ++ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ ++ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ ++ cJSON_Invalid\ ++) ++ ++/* Macro for iterating over an array or object */ ++#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) ++ ++/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ ++CJSON_PUBLIC(void *) cJSON_malloc(size_t size); ++CJSON_PUBLIC(void) cJSON_free(void *object); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/qtfs/rexec/rshim/rexec_shim.c b/qtfs/rexec/rshim/rexec_shim.c +new file mode 100644 +index 0000000..8e1b6ca +--- /dev/null ++++ b/qtfs/rexec/rshim/rexec_shim.c +@@ -0,0 +1,198 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cJSON.h" ++ ++#include "dirent.h" ++ ++#define RSHIM_LOG_FILE "/var/run/rexec/rexec_shim.log" ++#define rshim_log(info, ...) \ ++ do { \ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } while (0); ++ ++#define rshim_err(info, ...) \ ++ do { \ ++ time_t t; \ ++ struct tm *p; \ ++ time(&t); \ ++ p = localtime(&t); \ ++ printf("[%d/%02d/%02d %02d:%02d:%02d][LOG:%s:%3d]"info"\n", \ ++ p->tm_year + 1900, p->tm_mon+1, p->tm_mday, \ ++ p->tm_hour, p->tm_min, p->tm_sec, __func__, __LINE__, ##__VA_ARGS__); \ ++ } while (0); ++ ++void rshim_close_all_fd() ++{ ++ DIR *dir = NULL; ++ struct dirent *entry; ++ dir = opendir("/proc/self/fd/"); ++ if (dir == NULL) { ++ rshim_err("open path:/proc/self/fd/ failed"); ++ return; ++ } ++ while (entry = readdir(dir)) { ++ int fd = atoi(entry->d_name); ++ if (fd <= 2) ++ continue; ++ close(fd); ++ } ++ closedir(dir); ++ return; ++} ++ ++int rshim_get_file_size(char *file) ++{ ++ int size = 0; ++ FILE *f = fopen(file, "rb"); ++ if (f == NULL) { ++ rshim_err("File:%s fopen failed.", file); ++ return -1; ++ } ++ fseek(f, 0, SEEK_END); ++ size = ftell(f); ++ fclose(f); ++ return size; ++} ++ ++void rshim_reg_file_open(int fd_target, char *path, int perm, int offset) ++{ ++ int fd = open(path, perm); ++ int fd2 = -1; ++ if (fd < 0) { ++ rshim_err("Open file:%s failed, fd:%d err:%s", path, fd, strerror(errno)); ++ return; ++ } ++ if (fd != fd_target) { ++ fd2 = dup2(fd, fd_target); ++ if (fd2 != fd_target) { ++ rshim_err("Failed to open file:%s by fd:%d", path, fd_target); ++ close(fd2); ++ close(fd); ++ return; ++ } ++ close(fd); ++ } ++ int off = lseek(fd_target, offset, SEEK_SET); ++ if (off < 0) { ++ rshim_err("Failed to set offset:%d to file:%s, fd:%d, fd2:%d", offset, path, fd, fd2); ++ return; ++ } ++ rshim_log("Successed to set offset:%d to file:%s, fd:%d, fd2:%d", offset, path, fd, fd2); ++ return; ++} ++ ++void rshim_reg_file_resume(const char * const json_buf) ++{ ++ const cJSON *files; ++ const cJSON *file; ++ const cJSON *fd; ++ const cJSON *path; ++ const cJSON *perm; ++ const cJSON *offset; ++ int curfd = 3; // begin from 3 ++ cJSON *fd_json = cJSON_Parse(json_buf); ++ if (fd_json == NULL) ++ { ++ const char *error_ptr = cJSON_GetErrorPtr(); ++ if (error_ptr != NULL) ++ fprintf(stderr, "Error before: %s\n", error_ptr); ++ goto end; ++ } ++ files = cJSON_GetObjectItemCaseSensitive(fd_json, "Files"); ++ cJSON_ArrayForEach(file, files) { ++ fd = cJSON_GetObjectItemCaseSensitive(file, "Fd"); ++ path = cJSON_GetObjectItemCaseSensitive(file, "Path"); ++ perm = cJSON_GetObjectItemCaseSensitive(file, "Perm"); ++ offset = cJSON_GetObjectItemCaseSensitive(file, "Offset"); ++ rshim_log("Get file from json fd:%d path:%s perm:%d offset:%d", ++ fd->valueint, path->valuestring, perm->valueint, offset->valueint); ++ rshim_reg_file_open(fd->valueint, path->valuestring, perm->valueint, offset->valueint); ++ } ++ ++end: ++ cJSON_Delete(fd_json); ++ return; ++} ++ ++void rshim_reg_file_sync(char *json_file) ++{ ++#define RSHIM_JSON_SIZE_MAX 1024*1024*1024 // 限制为1M足够了 ++ rshim_close_all_fd(); ++ if (json_file == NULL) ++ return; ++ ++ int json_size = rshim_get_file_size(json_file); ++ if (json_size < 0 || json_size > RSHIM_JSON_SIZE_MAX) { ++ rshim_err("Get json file:%s size failed, size:%d.", json_file, json_size); ++ return; ++ } ++ rshim_log("Get json file:%s size:%d", json_file, json_size); ++ ++ char *json_buf = (char *)malloc(json_size + 1); ++ int json_len = 0; ++ if (json_buf == NULL) { ++ rshim_err("Malloc error."); ++ return; ++ } ++ memset(json_buf, 0, json_size + 1); ++ int json_fd = open(json_file, O_RDONLY); ++ if (json_fd < 0) { ++ rshim_err("Open json file:%s failed, err:%s", json_file, strerror(errno)); ++ goto end; ++ } ++ json_len = read(json_fd, json_buf, json_size + 1); ++ if (json_len <= 0) { ++ rshim_err("Failed to read from json file:%s, ret:%d err:%s", json_file, json_len, strerror(errno)); ++ close(json_fd); ++ goto end; ++ } ++ close(json_fd); ++ remove(json_file); ++ rshim_reg_file_resume((const char * const)json_buf); ++end: ++ free(json_buf); ++ return; ++} ++ ++/* ++ param list: ++ 1) -f xxx.json binary param1 param2 ... ++ 2) binary param1 param2... ++*/ ++int main(int argc, char *argv[]) ++{ ++ char *json_file = NULL; ++ char **newarg = NULL; ++ ++ // 至少得有需要执行的二进制名称 ++ if (argc < 2 || (strcmp(argv[1], "-f") == 0 && argc < 4)) { ++ rshim_err("Input argument list too short."); ++ return -1; ++ } ++ ++ if (strcmp(argv[1], "-f") == 0) { ++ json_file = argv[2]; ++ newarg = &argv[3]; ++ } else { ++ newarg = &argv[1]; ++ } ++ ++ rshim_reg_file_sync(json_file); ++ execvp(newarg[0], newarg); ++ perror("execve failed."); ++ ++ exit(EXIT_FAILURE); ++} ++ +diff --git a/qtfs/rexec/server.go b/qtfs/rexec/server.go +index de3f6cf..32a91de 100644 +--- a/qtfs/rexec/server.go ++++ b/qtfs/rexec/server.go +@@ -128,7 +128,16 @@ func main() { + } + return + } +- cmd := exec.Command(command.Cmd, command.Args...) ++ ++ args := []string{} ++ fdFilePath := restoreFileInfo(command.Cmd, command.Files) ++ if fdFilePath != "" { ++ args = append(args, "-f", fdFilePath) ++ } ++ args = append(args, command.Cmd) ++ args = append(args, command.Args...) ++ ++ cmd := exec.Command("/usr/bin/rexec_shim", args...) + cmd.Stdout = command.Stdout + cmd.Stderr = command.Stderr + cmd.Env = append([]string{}, command.Env...) +-- +2.33.0 + diff --git a/dpu-utilities.spec b/dpu-utilities.spec index 3a6ec0a..5bbb332 100644 --- a/dpu-utilities.spec +++ b/dpu-utilities.spec @@ -1,7 +1,7 @@ Name: dpu-utilities Summary: openEuler dpu utilities Version: 1.1 -Release: 3 +Release: 4 License: GPL-2.0 Source: https://gitee.com/openeuler/dpu-utilities/repository/archive/v%{version}.tar.gz ExclusiveOS: linux @@ -10,12 +10,17 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-root Conflicts: %{name} < %{version}-%{release} Provides: %{name} = %{version}-%{release} %define kernel_version %(ver=`rpm -qa|grep kernel-devel`;echo ${ver#*kernel-devel-}) -BuildRequires: kernel-devel >= 5.10, gcc, make +BuildRequires: kernel-devel >= 5.10, gcc, make, glib2-devel, glib2 BuildRequires: golang Patch0: 0001-add-path-put-in-xattr-set.patch Patch1: 0002-Add-drop-link-and-dentry-invalid-in-unlink-and-rmdir.patch Patch2: 0003-enable-rexec-read-net-addr-from-config-file.patch Patch3: 0004-Fix-inode-sync-error-between-client-and-server.patch +Patch4: 0005-Add-whitelist-of-qtfs.patch +Patch5: 0006-Fix-error-of-getxattr-and-listxattr.patch +Patch6: 0007-Add-whitelist-of-rexec.patch +Patch7: 0008-Add-udsproxy.patch +Patch8: 0009-Add-rexec-shim.patch %description This package contains the software utilities on dpu. @@ -50,15 +55,20 @@ cd %_builddir/%{name}-v%{version}/qtfs/qtfs_server make cd %_builddir/%{name}-v%{version}/qtfs/rexec make +cd %_builddir/%{name}-v%{version}/qtfs/ipc +make %install -mkdir -p $RPM_BUILD_ROOT/lib/modules/%{kernel_version}//extra +mkdir -p $RPM_BUILD_ROOT/lib/modules/%{kernel_version}/extra mkdir -p $RPM_BUILD_ROOT/usr/bin/ +mkdir -p ${RPM_BUILD_ROOT}/usr/lib64/ install %_builddir/%{name}-v%{version}/qtfs/qtfs/qtfs.ko $RPM_BUILD_ROOT/lib/modules/%{kernel_version}/extra install %_builddir/%{name}-v%{version}/qtfs/qtfs_server/qtfs_server.ko $RPM_BUILD_ROOT/lib/modules/%{kernel_version}/extra install -m 0700 %_builddir/%{name}-v%{version}/qtfs/qtfs_server/engine $RPM_BUILD_ROOT/usr/bin/ install -m 0700 %_builddir/%{name}-v%{version}/qtfs/rexec/rexec ${RPM_BUILD_ROOT}/usr/bin/ install -m 0700 %_builddir/%{name}-v%{version}/qtfs/rexec/rexec_server ${RPM_BUILD_ROOT}/usr/bin/ +install -m 0700 %_builddir/%{name}-v%{version}/qtfs/ipc/udsproxyd ${RPM_BUILD_ROOT}/usr/bin/ +install -m 0700 %_builddir/%{name}-v%{version}/qtfs/ipc/libudsproxy.so ${RPM_BUILD_ROOT}/usr/lib64/ mkdir -p $RPM_BUILD_ROOT/opt/imageTailor cp -rf %_builddir/%{name}-v%{version}/dpuos/image_tailor_cfg/custom $RPM_BUILD_ROOT/opt/imageTailor cp -rf %_builddir/%{name}-v%{version}/dpuos/image_tailor_cfg/kiwi $RPM_BUILD_ROOT/opt/imageTailor @@ -91,6 +101,8 @@ fi /lib/modules/%{kernel_version}/extra/qtfs.ko %attr(0700, root, root) /usr/bin/rexec_server %attr(0700, root, root) /usr/bin/rexec +%attr(0700, root, root) /usr/bin/udsproxyd +%attr(0700, root, root) /usr/lib64/libudsproxy.so %files -n qtfs-server /lib/modules/%{kernel_version}/extra/qtfs_server.ko @@ -108,6 +120,8 @@ sed -i '/# product cut_conf/a\dpuos kiwi/minios/cfg_dpuos yes' /opt/imageT sed -i '//a\dpuos 1 rpm-dir euler_base' /opt/imageTailor/repos/RepositoryRule.conf %changelog +* Thu Feb 09 2023 YangXin <245051644@qq.com> 1.1-4 +- Add whitelist to qtfs and rexec, fix errors, add udsproxy. * Thu Dec 15 2022 YangXin <245051644@qq.com> 1.1-3 - Fix inode sync error between client and server. * Thu Dec 08 2022 YangXin <245051644@qq.com> 1.1-2 diff --git a/v1.1.tar.gz b/v1.1.tar.gz old mode 100755 new mode 100644 -- Gitee From 3a0078cb93fc2f29f93d419669abbf6b10e159ec Mon Sep 17 00:00:00 2001 From: YangXin <245051644@qq.com> Date: Fri, 10 Feb 2023 14:00:05 +0000 Subject: [PATCH 2/2] Adapt to kernel 6.1 file system interface changes Signed-off-by: YangXin <245051644@qq.com> (cherry picked from commit e47fbf84ab514b89980fb191ebec9fecdde189fd) --- ...el-6.1-file-system-interface-changes.patch | 344 ++++++++++++++++++ dpu-utilities.spec | 1 + 2 files changed, 345 insertions(+) create mode 100644 0010-Adapt-to-kernel-6.1-file-system-interface-changes.patch diff --git a/0010-Adapt-to-kernel-6.1-file-system-interface-changes.patch b/0010-Adapt-to-kernel-6.1-file-system-interface-changes.patch new file mode 100644 index 0000000..b610481 --- /dev/null +++ b/0010-Adapt-to-kernel-6.1-file-system-interface-changes.patch @@ -0,0 +1,344 @@ +From e9f4fc2f658958a37f859a37084560c592c162ec Mon Sep 17 00:00:00 2001 +From: YangXin <245051644@qq.com> +Date: Fri, 10 Feb 2023 13:55:57 +0000 +Subject: [PATCH] Adapt to kernel 6.1 file system interface changes. + +Signed-off-by: YangXin <245051644@qq.com> +--- + qtfs/qtfs/ops.h | 2 +- + qtfs/qtfs/proc.c | 6 +++--- + qtfs/qtfs/qtfs-mod.c | 5 +++-- + qtfs/qtfs/sb.c | 43 ++++++++++++++++++++++------------------ + qtfs/qtfs/xattr.c | 4 ++++ + qtfs/qtfs_server/fsops.c | 16 +++++++-------- + 6 files changed, 43 insertions(+), 33 deletions(-) + +diff --git a/qtfs/qtfs/ops.h b/qtfs/qtfs/ops.h +index 5cab367..a18f4da 100644 +--- a/qtfs/qtfs/ops.h ++++ b/qtfs/qtfs/ops.h +@@ -15,7 +15,7 @@ bool is_sb_proc(struct super_block *sb); + struct inode *qtfs_iget(struct super_block *sb, struct inode_info *ii); + const char *qtfs_getlink(struct dentry *dentry, + struct inode *inode, struct delayed_call *done); +-int qtfs_getattr(const struct path *, struct kstat *, u32, unsigned int); ++int qtfs_getattr(struct user_namespace *mnt_userns, const struct path *, struct kstat *, u32, unsigned int); + struct dentry * qtfs_lookup(struct inode *, struct dentry *, unsigned int); + + #endif +diff --git a/qtfs/qtfs/proc.c b/qtfs/qtfs/proc.c +index 60401d9..e37303e 100644 +--- a/qtfs/qtfs/proc.c ++++ b/qtfs/qtfs/proc.c +@@ -9,7 +9,7 @@ + + struct dentry *qtfs_proc_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags); + const char *qtfs_proc_getlink(struct dentry *dentry, struct inode *inode, struct delayed_call *done); +-int qtfs_proc_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags); ++int qtfs_proc_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags); + + enum qtfs_type qtfs_get_type(char *str) + { +@@ -187,7 +187,7 @@ remote: + return qtfs_getlink(dentry, inode, done); + } + +-int qtfs_proc_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags) ++int qtfs_proc_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags) + { + char cpath[NAME_MAX] = {0}; + char tmp[NAME_MAX] = {0}; +@@ -223,5 +223,5 @@ int qtfs_proc_getattr(const struct path *path, struct kstat *stat, u32 req_mask, + } + + remote: +- return qtfs_getattr(path, stat, req_mask, flags); ++ return qtfs_getattr(NULL, path, stat, req_mask, flags); + } +diff --git a/qtfs/qtfs/qtfs-mod.c b/qtfs/qtfs/qtfs-mod.c +index abd9443..eb8d21a 100644 +--- a/qtfs/qtfs/qtfs-mod.c ++++ b/qtfs/qtfs/qtfs-mod.c +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + #include ++#include + #include "conn.h" + + #include "qtfs-mod.h" +@@ -114,7 +115,7 @@ connecting: + msleep(500); + } + if (pvar == NULL) { +- do_exit(0); ++ return 0; + } + qtfs_info("qtfs epoll thread establish a new connection."); + req = qtfs_sock_msg_buf(pvar, QTFS_RECV); +@@ -170,7 +171,7 @@ connecting: + qtfs_err("conn send failed, ret:%d\n", ret); + } + qtfs_epoll_cut_conn(pvar); +- do_exit(0); ++ return 0; + } + + struct file_operations qtfs_misc_fops = { +diff --git a/qtfs/qtfs/sb.c b/qtfs/qtfs/sb.c +index 104d137..38cac43 100644 +--- a/qtfs/qtfs/sb.c ++++ b/qtfs/qtfs/sb.c +@@ -461,13 +461,12 @@ static vm_fault_t qtfs_vm_fault(struct vm_fault *vmf) + return ret; + } + +-static void qtfs_map_pages(struct vm_fault *vmf, ++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); + +- filemap_map_pages(vmf, start_pgoff, end_pgoff); +- return; ++ return filemap_map_pages(vmf, start_pgoff, end_pgoff); + } + + static vm_fault_t qtfs_page_mkwrite(struct vm_fault *vmf) +@@ -665,7 +664,6 @@ static struct file_operations qtfs_file_ops = { + .poll = qtfsfifo_poll, + }; + +- + static int qtfs_readpage(struct file *file, struct page *page) + { + void *kaddr = NULL; +@@ -682,6 +680,14 @@ static int qtfs_readpage(struct file *file, struct page *page) + return 0; + } + ++static int qtfs_read_folio(struct file *file, struct folio *folio) ++{ ++ struct page *page = &folio->page; ++ qtfs_readpage(file, page); ++ ++ return 0; ++} ++ + static struct page **qtfs_alloc_pages(unsigned int nr) + { + struct page **pages = kzalloc(nr * (sizeof(struct page *)), GFP_KERNEL); +@@ -726,19 +732,18 @@ static int qtfs_writepages(struct address_space *mapping, + return 0; + } + +-static int qtfs_setpagedirty(struct page *page) ++static bool qtfs_dirty_folio(struct address_space *mapping, struct folio *folio) + { + qtfs_info("qtfs set page dirty."); +- __set_page_dirty_nobuffers(page); +- return 0; ++ return filemap_dirty_folio(mapping, folio); + } + + static const struct address_space_operations qtfs_aops = { +- .readpage = qtfs_readpage, ++ .read_folio = qtfs_read_folio, + .readahead = qtfs_readahead, + .writepage = qtfs_writepage, + .writepages = qtfs_writepages, +- .set_page_dirty = qtfs_setpagedirty, ++ .dirty_folio = qtfs_dirty_folio, + }; + + int qtfs_new_entry(struct inode *inode, struct dentry *dentry) +@@ -758,7 +763,7 @@ int qtfs_new_entry(struct inode *inode, struct dentry *dentry) + return 0; + } + +-int qtfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++int qtfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_mkdir *req = NULL; +@@ -793,7 +798,7 @@ int qtfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) + return ret; + } + +-int qtfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) ++int qtfs_create(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_icreate *req; +@@ -833,7 +838,7 @@ int qtfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool exc + return ret ? ret : ret2; + } + +-int qtfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) ++int qtfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_mknod *req; +@@ -1093,7 +1098,7 @@ err_end: + return error; + } + +-int qtfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++int qtfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry, const char *symname) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_symlink *req; +@@ -1138,7 +1143,7 @@ err_end: + return error; + } + +-int qtfs_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags) ++int qtfs_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat, u32 req_mask, unsigned int flags) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_getattr *req; +@@ -1182,7 +1187,7 @@ int qtfs_getattr(const struct path *path, struct kstat *stat, u32 req_mask, unsi + return 0; + } + +-int qtfs_setattr(struct dentry *dentry, struct iattr *attr) ++int qtfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *attr) + { + struct qtfs_sock_var_s *pvar = qtfs_conn_get_param(); + struct qtreq_setattr *req; +@@ -1277,9 +1282,9 @@ const char *qtfs_getlink(struct dentry *dentry, + return link; + } + +-int qtfs_rename(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry, +- unsigned int flags) ++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) + { + struct qtreq_rename *req; + struct qtrsp_rename *rsp; +@@ -1364,7 +1369,7 @@ static int qtfs_fill_super(struct super_block *sb, void *priv_data, int silent) + root_inode = new_inode(sb); + root_inode->i_ino = 1; + +- inode_init_owner(root_inode, NULL, mode); ++ inode_init_owner(&init_user_ns, root_inode, NULL, mode); + root_inode->i_sb = sb; + if (priv->type == QTFS_PROC) { + qtfs_info("qtfs type: proc\n"); +diff --git a/qtfs/qtfs/xattr.c b/qtfs/qtfs/xattr.c +index a2a605d..61153c0 100644 +--- a/qtfs/qtfs/xattr.c ++++ b/qtfs/qtfs/xattr.c +@@ -59,6 +59,7 @@ static int qtfs_xattr_set(const struct xattr_handler *handler, + + static int + qtfs_xattr_user_set(const struct xattr_handler *handler, ++ struct user_namespace *mnt_userns, + struct dentry *unused, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +@@ -69,6 +70,7 @@ qtfs_xattr_user_set(const struct xattr_handler *handler, + + static int + qtfs_xattr_trusted_set(const struct xattr_handler *handler, ++ struct user_namespace *mnt_userns, + struct dentry *unused, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +@@ -78,6 +80,7 @@ qtfs_xattr_trusted_set(const struct xattr_handler *handler, + + static int + qtfs_xattr_security_set(const struct xattr_handler *handler, ++ struct user_namespace *mnt_userns, + struct dentry *unused, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +@@ -87,6 +90,7 @@ qtfs_xattr_security_set(const struct xattr_handler *handler, + + static int + qtfs_xattr_hurd_set(const struct xattr_handler *handler, ++ struct user_namespace *mnt_userns, + struct dentry *unused, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +diff --git a/qtfs/qtfs_server/fsops.c b/qtfs/qtfs_server/fsops.c +index 6c3e201..87caf90 100644 +--- a/qtfs/qtfs_server/fsops.c ++++ b/qtfs/qtfs_server/fsops.c +@@ -431,7 +431,7 @@ static int handle_lookup(struct qtserver_arg *arg) + return sizeof(struct qtrsp_lookup); + } + +-static int qtfs_filldir(struct dir_context *ctx, const char *name, int namelen, ++static bool qtfs_filldir(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) + { + struct qtfs_dirent64 *dirent, *prev; +@@ -440,7 +440,7 @@ static int qtfs_filldir(struct dir_context *ctx, const char *name, int namelen, + int prev_reclen; + + if (reclen > buf->count) +- return -EINVAL; ++ return false; + + prev_reclen = buf->prev_reclen; + dirent = buf->dir; +@@ -455,7 +455,7 @@ static int qtfs_filldir(struct dir_context *ctx, const char *name, int namelen, + buf->dir = (void *)dirent + reclen; + buf->count -= reclen; + buf->vldcnt++; +- return 0; ++ return true; + } + + static int handle_readdir(struct qtserver_arg *arg) +@@ -642,7 +642,7 @@ static int handle_setattr(struct qtserver_arg *arg) + } + + inode_lock(inode); +- rsp->errno = notify_change(path.dentry, &req->attr, NULL); ++ rsp->errno = notify_change(&init_user_ns, path.dentry, &req->attr, NULL); + if (rsp->errno < 0) { + rsp->ret = QTFS_ERR; + qtfs_err("handle setattr, path:<%s> failed with %d.\n", req->path, ret); +@@ -716,7 +716,7 @@ retry: + req->mode &= ~current_umask(); + error = security_path_mknod(&path, dent, req->mode, req->dev); + if (!error) +- error = vfs_mknod(path.dentry->d_inode, dent, req->mode, req->dev); ++ error = vfs_mknod(&init_user_ns, path.dentry->d_inode, dent, req->mode, req->dev); + done_path_create(&path, dent); + if (error == -ESTALE && !(flags & LOOKUP_REVAL)) { + flags |= LOOKUP_REVAL; +@@ -794,7 +794,7 @@ retry: + return sizeof(struct qtrsp_symlink); + } + +- rsp->errno = vfs_symlink(path.dentry->d_inode, dentry, oldname); ++ rsp->errno = vfs_symlink(&init_user_ns, path.dentry->d_inode, dentry, oldname); + done_path_create(&path, dentry); + if (rsp->errno == -ESTALE && !(lookup_flags & LOOKUP_REVAL)) { + lookup_flags |= LOOKUP_REVAL; +@@ -922,7 +922,7 @@ int handle_xattrset(struct qtserver_arg *arg) + goto err_handle; + } + +- rsp->errno = vfs_setxattr(path.dentry, &req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], req->d.size, req->d.flags); ++ rsp->errno = vfs_setxattr(&init_user_ns, path.dentry, &req->buf[req->d.pathlen], &req->buf[req->d.pathlen + req->d.namelen], req->d.size, req->d.flags); + 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.size, req->d.flags); +@@ -963,7 +963,7 @@ int handle_xattrget(struct qtserver_arg *arg) + } + } + +- error = vfs_getxattr(path.dentry, req->d.prefix_name, kvalue, req->d.size); ++ error = vfs_getxattr(&init_user_ns, path.dentry, req->d.prefix_name, kvalue, req->d.size); + path_put(&path); + if (error > 0) { + if (req->d.pos >= error) { +-- +2.33.0 + diff --git a/dpu-utilities.spec b/dpu-utilities.spec index 5bbb332..71e9d31 100644 --- a/dpu-utilities.spec +++ b/dpu-utilities.spec @@ -21,6 +21,7 @@ Patch5: 0006-Fix-error-of-getxattr-and-listxattr.patch Patch6: 0007-Add-whitelist-of-rexec.patch Patch7: 0008-Add-udsproxy.patch Patch8: 0009-Add-rexec-shim.patch +Patch9: 0010-Adapt-to-kernel-6.1-file-system-interface-changes.patch %description This package contains the software utilities on dpu. -- Gitee