diff options
Diffstat (limited to 'sys/kern/uipc_mqueue.c')
-rw-r--r-- | sys/kern/uipc_mqueue.c | 365 |
1 files changed, 303 insertions, 62 deletions
diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index c44ab5a..a34c228 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -45,6 +45,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -1622,7 +1624,7 @@ mqueue_send(struct mqueue *mq, const char *msg_ptr, const struct timespec *abs_timeout) { struct mqueue_msg *msg; - struct timespec ets, ts, ts2; + struct timespec ts, ts2; struct timeval tv; int error; @@ -1658,15 +1660,12 @@ mqueue_send(struct mqueue *mq, const char *msg_ptr, if (error != EAGAIN) goto bad; - error = copyin(abs_timeout, &ets, sizeof(ets)); - if (error != 0) - goto bad; - if (ets.tv_nsec >= 1000000000 || ets.tv_nsec < 0) { + if (abs_timeout->tv_nsec >= 1000000000 || abs_timeout->tv_nsec < 0) { error = EINVAL; goto bad; } for (;;) { - ts2 = ets; + ts2 = *abs_timeout; getnanotime(&ts); timespecsub(&ts2, &ts); if (ts2.tv_sec < 0 || (ts2.tv_sec == 0 && ts2.tv_nsec <= 0)) { @@ -1773,7 +1772,7 @@ mqueue_receive(struct mqueue *mq, char *msg_ptr, const struct timespec *abs_timeout) { struct mqueue_msg *msg; - struct timespec ets, ts, ts2; + struct timespec ts, ts2; struct timeval tv; int error; @@ -1804,16 +1803,13 @@ mqueue_receive(struct mqueue *mq, char *msg_ptr, if (error != EAGAIN) return (error); - error = copyin(abs_timeout, &ets, sizeof(ets)); - if (error != 0) - return (error); - if (ets.tv_nsec >= 1000000000 || ets.tv_nsec < 0) { + if (abs_timeout->tv_nsec >= 1000000000 || abs_timeout->tv_nsec < 0) { error = EINVAL; return (error); } for (;;) { - ts2 = ets; + ts2 = *abs_timeout; getnanotime(&ts); timespecsub(&ts2, &ts); if (ts2.tv_sec < 0 || (ts2.tv_sec == 0 && ts2.tv_nsec <= 0)) { @@ -1934,40 +1930,28 @@ notifier_remove(struct proc *p, struct mqueue *mq, int fd) PROC_UNLOCK(p); } -/* - * Syscall to open a message queue. - */ -int -kmq_open(struct thread *td, struct kmq_open_args *uap) +static int +kern_kmq_open(struct thread *td, const char *upath, int flags, mode_t mode, + const struct mq_attr *attr) { char path[MQFS_NAMELEN + 1]; - struct mq_attr attr, *pattr; struct mqfs_node *pn; struct filedesc *fdp; struct file *fp; struct mqueue *mq; - int fd, error, len, flags, cmode; - - if ((uap->flags & O_ACCMODE) == O_ACCMODE) - return (EINVAL); + int fd, error, len, cmode; fdp = td->td_proc->p_fd; - flags = FFLAGS(uap->flags); - cmode = (((uap->mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT); + cmode = (((mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT); mq = NULL; - if ((flags & O_CREAT) && (uap->attr != NULL)) { - error = copyin(uap->attr, &attr, sizeof(attr)); - if (error) - return (error); - if (attr.mq_maxmsg <= 0 || attr.mq_maxmsg > maxmsg) + if ((flags & O_CREAT) != 0 && attr != NULL) { + if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > maxmsg) return (EINVAL); - if (attr.mq_msgsize <= 0 || attr.mq_msgsize > maxmsgsize) + if (attr->mq_msgsize <= 0 || attr->mq_msgsize > maxmsgsize) return (EINVAL); - pattr = &attr; - } else - pattr = NULL; + } - error = copyinstr(uap->path, path, MQFS_NAMELEN + 1, NULL); + error = copyinstr(upath, path, MQFS_NAMELEN + 1, NULL); if (error) return (error); @@ -1990,7 +1974,7 @@ kmq_open(struct thread *td, struct kmq_open_args *uap) if (!(flags & O_CREAT)) { error = ENOENT; } else { - mq = mqueue_alloc(pattr); + mq = mqueue_alloc(attr); if (mq == NULL) { error = ENFILE; } else { @@ -2045,6 +2029,27 @@ kmq_open(struct thread *td, struct kmq_open_args *uap) } /* + * Syscall to open a message queue. + */ +int +kmq_open(struct thread *td, struct kmq_open_args *uap) +{ + struct mq_attr attr; + int flags, error; + + if ((uap->flags & O_ACCMODE) == O_ACCMODE) + return (EINVAL); + flags = FFLAGS(uap->flags); + if ((flags & O_CREAT) != 0 && uap->attr != NULL) { + error = copyin(uap->attr, &attr, sizeof(attr)); + if (error) + return (error); + } + return (kern_kmq_open(td, uap->path, flags, uap->mode, + uap->attr != NULL ? &attr : NULL)); +} + +/* * Syscall to unlink a message queue. */ int @@ -2120,39 +2125,52 @@ getmq_write(struct thread *td, int fd, struct file **fpp, return _getmq(td, fd, fget_write, fpp, ppn, pmq); } -int -kmq_setattr(struct thread *td, struct kmq_setattr_args *uap) +static int +kern_kmq_setattr(struct thread *td, int mqd, const struct mq_attr *attr, + struct mq_attr *oattr) { struct mqueue *mq; struct file *fp; - struct mq_attr attr, oattr; u_int oflag, flag; int error; - if (uap->attr) { - error = copyin(uap->attr, &attr, sizeof(attr)); - if (error) - return (error); - if (attr.mq_flags & ~O_NONBLOCK) - return (EINVAL); - } - error = getmq(td, uap->mqd, &fp, NULL, &mq); + if (attr != NULL && (attr->mq_flags & ~O_NONBLOCK) != 0) + return (EINVAL); + error = getmq(td, mqd, &fp, NULL, &mq); if (error) return (error); - oattr.mq_maxmsg = mq->mq_maxmsg; - oattr.mq_msgsize = mq->mq_msgsize; - oattr.mq_curmsgs = mq->mq_curmsgs; - if (uap->attr) { + oattr->mq_maxmsg = mq->mq_maxmsg; + oattr->mq_msgsize = mq->mq_msgsize; + oattr->mq_curmsgs = mq->mq_curmsgs; + if (attr != NULL) { do { oflag = flag = fp->f_flag; flag &= ~O_NONBLOCK; - flag |= (attr.mq_flags & O_NONBLOCK); + flag |= (attr->mq_flags & O_NONBLOCK); } while (atomic_cmpset_int(&fp->f_flag, oflag, flag) == 0); } else oflag = fp->f_flag; - oattr.mq_flags = (O_NONBLOCK & oflag); + oattr->mq_flags = (O_NONBLOCK & oflag); fdrop(fp, td); - if (uap->oattr) + return (error); +} + +int +kmq_setattr(struct thread *td, struct kmq_setattr_args *uap) +{ + struct mq_attr attr, oattr; + int error; + + if (uap->attr != NULL) { + error = copyin(uap->attr, &attr, sizeof(attr)); + if (error != 0) + return (error); + } + error = kern_kmq_setattr(td, uap->mqd, uap->attr != NULL ? &attr : NULL, + &oattr); + if (error != 0) + return (error); + if (uap->oattr != NULL) error = copyout(&oattr, uap->oattr, sizeof(oattr)); return (error); } @@ -2162,15 +2180,23 @@ kmq_timedreceive(struct thread *td, struct kmq_timedreceive_args *uap) { struct mqueue *mq; struct file *fp; + struct timespec *abs_timeout, ets; int error; int waitok; error = getmq_read(td, uap->mqd, &fp, NULL, &mq); if (error) return (error); + if (uap->abs_timeout != NULL) { + error = copyin(uap->abs_timeout, &ets, sizeof(ets)); + if (error != 0) + return (error); + abs_timeout = &ets; + } else + abs_timeout = NULL; waitok = !(fp->f_flag & O_NONBLOCK); error = mqueue_receive(mq, uap->msg_ptr, uap->msg_len, - uap->msg_prio, waitok, uap->abs_timeout); + uap->msg_prio, waitok, abs_timeout); fdrop(fp, td); return (error); } @@ -2180,14 +2206,22 @@ kmq_timedsend(struct thread *td, struct kmq_timedsend_args *uap) { struct mqueue *mq; struct file *fp; + struct timespec *abs_timeout, ets; int error, waitok; error = getmq_write(td, uap->mqd, &fp, NULL, &mq); if (error) return (error); + if (uap->abs_timeout != NULL) { + error = copyin(uap->abs_timeout, &ets, sizeof(ets)); + if (error != 0) + return (error); + abs_timeout = &ets; + } else + abs_timeout = NULL; waitok = !(fp->f_flag & O_NONBLOCK); error = mqueue_send(mq, uap->msg_ptr, uap->msg_len, - uap->msg_prio, waitok, uap->abs_timeout); + uap->msg_prio, waitok, abs_timeout); fdrop(fp, td); return (error); } @@ -2511,12 +2545,219 @@ static struct vfsops mqfs_vfsops = { .vfs_statfs = mqfs_statfs, }; -SYSCALL_MODULE_HELPER(kmq_open); -SYSCALL_MODULE_HELPER(kmq_setattr); -SYSCALL_MODULE_HELPER(kmq_timedsend); -SYSCALL_MODULE_HELPER(kmq_timedreceive); -SYSCALL_MODULE_HELPER(kmq_notify); -SYSCALL_MODULE_HELPER(kmq_unlink); +static struct vfsconf mqueuefs_vfsconf = { + .vfc_version = VFS_VERSION, + .vfc_name = "mqueuefs", + .vfc_vfsops = &mqfs_vfsops, + .vfc_typenum = -1, + .vfc_flags = VFCF_SYNTHETIC +}; + +static struct syscall_helper_data mq_syscalls[] = { + SYSCALL_INIT_HELPER(kmq_open), + SYSCALL_INIT_HELPER(kmq_setattr), + SYSCALL_INIT_HELPER(kmq_timedsend), + SYSCALL_INIT_HELPER(kmq_timedreceive), + SYSCALL_INIT_HELPER(kmq_notify), + SYSCALL_INIT_HELPER(kmq_unlink), + SYSCALL_INIT_LAST +}; + +#ifdef COMPAT_FREEBSD32 +#include <compat/freebsd32/freebsd32.h> +#include <compat/freebsd32/freebsd32_proto.h> +#include <compat/freebsd32/freebsd32_syscall.h> +#include <compat/freebsd32/freebsd32_util.h> + +static void +mq_attr_from32(const struct mq_attr32 *from, struct mq_attr *to) +{ + + to->mq_flags = from->mq_flags; + to->mq_maxmsg = from->mq_maxmsg; + to->mq_msgsize = from->mq_msgsize; + to->mq_curmsgs = from->mq_curmsgs; +} + +static void +mq_attr_to32(const struct mq_attr *from, struct mq_attr32 *to) +{ + + to->mq_flags = from->mq_flags; + to->mq_maxmsg = from->mq_maxmsg; + to->mq_msgsize = from->mq_msgsize; + to->mq_curmsgs = from->mq_curmsgs; +} + +int +freebsd32_kmq_open(struct thread *td, struct freebsd32_kmq_open_args *uap) +{ + struct mq_attr attr; + struct mq_attr32 attr32; + int flags, error; + + if ((uap->flags & O_ACCMODE) == O_ACCMODE) + return (EINVAL); + flags = FFLAGS(uap->flags); + if ((flags & O_CREAT) != 0 && uap->attr != NULL) { + error = copyin(uap->attr, &attr32, sizeof(attr32)); + if (error) + return (error); + mq_attr_from32(&attr32, &attr); + } + return (kern_kmq_open(td, uap->path, flags, uap->mode, + uap->attr != NULL ? &attr : NULL)); +} + +int +freebsd32_kmq_setattr(struct thread *td, struct freebsd32_kmq_setattr_args *uap) +{ + struct mq_attr attr, oattr; + struct mq_attr32 attr32, oattr32; + int error; + + if (uap->attr != NULL) { + error = copyin(uap->attr, &attr32, sizeof(attr32)); + if (error != 0) + return (error); + mq_attr_from32(&attr32, &attr); + } + error = kern_kmq_setattr(td, uap->mqd, uap->attr != NULL ? &attr : NULL, + &oattr); + if (error != 0) + return (error); + if (uap->oattr != NULL) { + mq_attr_to32(&oattr, &oattr32); + error = copyout(&oattr32, uap->oattr, sizeof(oattr32)); + } + return (error); +} + +int +freebsd32_kmq_timedsend(struct thread *td, + struct freebsd32_kmq_timedsend_args *uap) +{ + struct mqueue *mq; + struct file *fp; + struct timespec32 ets32; + struct timespec *abs_timeout, ets; + int error; + int waitok; + + error = getmq_read(td, uap->mqd, &fp, NULL, &mq); + if (error) + return (error); + if (uap->abs_timeout != NULL) { + error = copyin(uap->abs_timeout, &ets32, sizeof(ets32)); + if (error != 0) + return (error); + CP(ets32, ets, tv_sec); + CP(ets32, ets, tv_nsec); + abs_timeout = &ets; + } else + abs_timeout = NULL; + waitok = !(fp->f_flag & O_NONBLOCK); + error = mqueue_send(mq, uap->msg_ptr, uap->msg_len, + uap->msg_prio, waitok, abs_timeout); + fdrop(fp, td); + return (error); +} + +int +freebsd32_kmq_timedreceive(struct thread *td, + struct freebsd32_kmq_timedreceive_args *uap) +{ + struct mqueue *mq; + struct file *fp; + struct timespec32 ets32; + struct timespec *abs_timeout, ets; + int error, waitok; + + error = getmq_write(td, uap->mqd, &fp, NULL, &mq); + if (error) + return (error); + if (uap->abs_timeout != NULL) { + error = copyin(uap->abs_timeout, &ets32, sizeof(ets32)); + if (error != 0) + return (error); + CP(ets32, ets, tv_sec); + CP(ets32, ets, tv_nsec); + abs_timeout = &ets; + } else + abs_timeout = NULL; + waitok = !(fp->f_flag & O_NONBLOCK); + error = mqueue_receive(mq, uap->msg_ptr, uap->msg_len, + uap->msg_prio, waitok, abs_timeout); + fdrop(fp, td); + return (error); +} + +static struct syscall_helper_data mq32_syscalls[] = { + SYSCALL32_INIT_HELPER(freebsd32_kmq_open), + SYSCALL32_INIT_HELPER(freebsd32_kmq_setattr), + SYSCALL32_INIT_HELPER(freebsd32_kmq_timedsend), + SYSCALL32_INIT_HELPER(freebsd32_kmq_timedreceive), + SYSCALL32_INIT_HELPER(kmq_notify), + SYSCALL32_INIT_HELPER(kmq_unlink), + SYSCALL_INIT_LAST +}; +#endif + +static int +mqinit(void) +{ + int error; + + error = syscall_helper_register(mq_syscalls); + if (error != 0) + return (error); +#ifdef COMPAT_FREEBSD32 + error = syscall32_helper_register(mq32_syscalls); + if (error != 0) + return (error); +#endif + return (0); +} + +static int +mqunload(void) +{ + +#ifdef COMPAT_FREEBSD32 + syscall32_helper_unregister(mq32_syscalls); +#endif + syscall_helper_unregister(mq_syscalls); + return (0); +} + +static int +mq_modload(struct module *module, int cmd, void *arg) +{ + int error = 0; + + error = vfs_modevent(module, cmd, arg); + if (error != 0) + return (error); -VFS_SET(mqfs_vfsops, mqueuefs, VFCF_SYNTHETIC); + switch (cmd) { + case MOD_LOAD: + error = mqinit(); + if (error != 0) + mqunload(); + break; + case MOD_UNLOAD: + error = mqunload(); + break; + default: + break; + } + return (error); +} + +static moduledata_t mqueuefs_mod = { + "mqueuefs", + mq_modload, + &mqueuefs_vfsconf +}; +DECLARE_MODULE(mqueuefs, mqueuefs_mod, SI_SUB_VFS, SI_ORDER_MIDDLE); MODULE_VERSION(mqueuefs, 1); |