Home | History | Annotate | Line # | Download | only in common
      1 /*	$NetBSD: linux_mqueue.c,v 1.2 2025/05/17 19:00:56 andvar Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2024 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: linux_mqueue.c,v 1.2 2025/05/17 19:00:56 andvar Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/filedesc.h>
     34 #include <sys/fcntl.h>
     35 #include <sys/mqueue.h>
     36 #include <sys/syscallargs.h>
     37 
     38 #include <compat/linux/common/linux_types.h>
     39 #include <compat/linux/common/linux_sched.h>
     40 #include <compat/linux/common/linux_fcntl.h>
     41 #include <compat/linux/common/linux_ipc.h>
     42 #include <compat/linux/common/linux_sem.h>
     43 #include <compat/linux/common/linux_signal.h>
     44 #include <compat/linux/common/linux_sigevent.h>
     45 #include <compat/linux/common/linux_util.h>
     46 #include <compat/linux/common/linux_mqueue.h>
     47 
     48 #include <compat/linux/linux_syscallargs.h>
     49 #include <compat/linux/linux_syscall.h>
     50 
     51 static void
     52 linux_to_bsd_mq_attr(const struct linux_mq_attr *lmp, struct mq_attr *bmp)
     53 {
     54 	memset(bmp, 0, sizeof(*bmp));
     55 	bmp->mq_flags = cvtto_bsd_mask(lmp->mq_flags, LINUX_O_NONBLOCK,
     56 	    O_NONBLOCK);
     57 	bmp->mq_maxmsg = lmp->mq_maxmsg;
     58 	bmp->mq_msgsize = lmp->mq_msgsize;
     59 	bmp->mq_curmsgs = lmp->mq_curmsgs;
     60 }
     61 
     62 static void
     63 bsd_to_linux_mq_attr(const struct mq_attr *bmp, struct linux_mq_attr *lmp)
     64 {
     65 	memset(lmp, 0, sizeof(*lmp));
     66 	lmp->mq_flags = cvtto_linux_mask(bmp->mq_flags, O_NONBLOCK,
     67 	    LINUX_O_NONBLOCK);
     68 	lmp->mq_maxmsg = bmp->mq_maxmsg;
     69 	lmp->mq_msgsize = bmp->mq_msgsize;
     70 	lmp->mq_curmsgs = bmp->mq_curmsgs;
     71 }
     72 
     73 /* Adapted from sys_mq_open */
     74 int
     75 linux_sys_mq_open(struct lwp *l, const struct linux_sys_mq_open_args *uap,
     76     register_t *retval)
     77 {
     78 	/* {
     79 		syscallarg(const char *) name;
     80 		syscallarg(int) oflag;
     81 		syscallarg(linux_umode_t) mode;
     82 		syscallarg(struct linux_mq_attr *) attr;
     83 	} */
     84 	struct linux_mq_attr lattr;
     85 	struct mq_attr *attr = NULL, a;
     86 	int error, oflag;
     87 
     88 	oflag = linux_to_bsd_ioflags(SCARG(uap, oflag));
     89 
     90 	if ((oflag & O_CREAT) != 0 && SCARG(uap, attr) != NULL) {
     91 		error = copyin(SCARG(uap, attr), &lattr, sizeof(lattr));
     92 		if (error)
     93 			return error;
     94 		linux_to_bsd_mq_attr(&lattr, &a);
     95 		attr = &a;
     96 	}
     97 
     98 	return mq_handle_open(l, SCARG(uap, name), oflag,
     99 	    (mode_t)SCARG(uap, mode), attr, retval);
    100 }
    101 
    102 int
    103 linux_sys_mq_unlink(struct lwp *l, const struct linux_sys_mq_unlink_args *uap,
    104     register_t *retval)
    105 {
    106 	/* {
    107 		syscallarg(const char *) name;
    108 	} */
    109 	struct sys_mq_unlink_args args;
    110 
    111 	SCARG(&args, name) = SCARG(uap, name);
    112 
    113 	return sys_mq_unlink(l, &args, retval);
    114 }
    115 
    116 /* Adapted from sys___mq_timedsend50 */
    117 int
    118 linux_sys_mq_timedsend(struct lwp *l, const struct linux_sys_mq_timedsend_args *uap,
    119     register_t *retval)
    120 {
    121 	/* {
    122 		syscallarg(linux_mqd_t) mqdes;
    123 		syscallarg(const char *) msg_ptr;
    124 		syscallarg(size_t) msg_len;
    125 		syscallarg(unsigned int) msg_prio;
    126 		syscallarg(const struct linux_timespec *) abs_timeout;
    127 	} */
    128 	struct linux_timespec lts;
    129 	struct timespec ts, *tsp;
    130 	int error;
    131 
    132 	/* Get and convert time value */
    133 	if (SCARG(uap, abs_timeout)) {
    134 		error = copyin(SCARG(uap, abs_timeout), &lts, sizeof(lts));
    135 		if (error)
    136 			return error;
    137 		linux_to_native_timespec(&ts, &lts);
    138 		tsp = &ts;
    139 	} else {
    140 		tsp = NULL;
    141 	}
    142 
    143 	return mq_send1((mqd_t)SCARG(uap, mqdes), SCARG(uap, msg_ptr),
    144 	    SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp);
    145 }
    146 
    147 /* Adapted from sys___mq_timedreceive50 */
    148 int
    149 linux_sys_mq_timedreceive(struct lwp *l,
    150     const struct linux_sys_mq_timedreceive_args *uap, register_t *retval)
    151 {
    152 	/* {
    153 		syscallarg(linux_mqd_t) mqdes;
    154 		syscallarg(char *) msg_ptr;
    155 		syscallarg(size_t) msg_len;
    156 		syscallarg(unsigned int *) msg_prio;
    157 		syscallarg(const struct linux_timespec *) abs_timeout;
    158 	}; */
    159 	struct linux_timespec lts;
    160 	struct timespec ts, *tsp;
    161 	ssize_t mlen;
    162 	int error;
    163 
    164 	/* Get and convert time value */
    165 	if (SCARG(uap, abs_timeout)) {
    166 		error = copyin(SCARG(uap, abs_timeout), &lts, sizeof(lts));
    167 		if (error)
    168 			return error;
    169 		linux_to_native_timespec(&ts, &lts);
    170 		tsp = &ts;
    171 	} else {
    172 		tsp = NULL;
    173 	}
    174 
    175 	error = mq_recv1((mqd_t)SCARG(uap, mqdes), SCARG(uap, msg_ptr),
    176 	    SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp, &mlen);
    177 	if (error == 0)
    178 		*retval = mlen;
    179 
    180 	return error;
    181 }
    182 
    183 /* Adapted from sys_mq_notify */
    184 int
    185 linux_sys_mq_notify(struct lwp *l, const struct linux_sys_mq_notify_args *uap,
    186     register_t *retval)
    187 {
    188 	/* {
    189 		syscallarg(linux_mqd_t) mqdes;
    190 		syscallarg(const struct linux_sigevent *) sevp;
    191 	} */
    192 	struct mqueue *mq;
    193 	struct sigevent sig;
    194 	int error;
    195 
    196 	if (SCARG(uap, sevp)) {
    197 		/* Get the signal from user-space */
    198 		error = linux_sigevent_copyin(SCARG(uap, sevp), &sig,
    199 		    sizeof(sig));
    200 		if (error)
    201 			return error;
    202 		if (sig.sigev_notify == SIGEV_SIGNAL &&
    203 		    (sig.sigev_signo <= 0 || sig.sigev_signo >= NSIG))
    204 			return EINVAL;
    205 	}
    206 
    207 	error = mqueue_get((mqd_t)SCARG(uap, mqdes), 0, &mq);
    208 	if (error)
    209 		return error;
    210 	if (SCARG(uap, sevp)) {
    211 		/* Register notification: set the signal and target process */
    212 		if (mq->mq_notify_proc == NULL) {
    213 			memcpy(&mq->mq_sig_notify, &sig,
    214 			    sizeof(struct sigevent));
    215 			mq->mq_notify_proc = l->l_proc;
    216 		} else {
    217 			/* Fail if someone else already registered */
    218 			error = EBUSY;
    219 		}
    220 	} else {
    221 		/* Unregister the notification */
    222 		mq->mq_notify_proc = NULL;
    223 	}
    224 	mutex_exit(&mq->mq_mtx);
    225 	fd_putfile((int)SCARG(uap, mqdes));
    226 
    227 	return error;
    228 }
    229 
    230 /* Adapted from sys_mq_getattr */
    231 static int
    232 linux_sys_mq_getattr(struct lwp *l,
    233     const struct linux_sys_mq_getsetattr_args *uap, register_t *retval)
    234 {
    235 	/* {
    236 		syscallarg(linux_mqd_t) mqdes;
    237 		syscallarg(const struct linux_mq_attr *) newattr;
    238 		syscallarg(struct linux_mq_attr *) oldattr;
    239 	} */
    240 	struct linux_mq_attr lattr;
    241 	struct mq_attr attr;
    242 	struct mqueue *mq;
    243 	int error;
    244 
    245 	error = mqueue_get((mqd_t)SCARG(uap, mqdes), 0, &mq);
    246 	if (error)
    247 		return error;
    248 	memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr));
    249 	bsd_to_linux_mq_attr(&attr, &lattr);
    250 	mutex_exit(&mq->mq_mtx);
    251 	fd_putfile((int)SCARG(uap, mqdes));
    252 
    253 	return copyout(&lattr, SCARG(uap, oldattr), sizeof(lattr));
    254 }
    255 
    256 /* Adapted from sys_mq_setattr */
    257 static int
    258 linux_sys_mq_setattr(struct lwp *l,
    259     const struct linux_sys_mq_getsetattr_args *uap, register_t *retval)
    260 {
    261 	/* {
    262 		syscallarg(linux_mqd_t) mqdes;
    263 		syscallarg(const struct linux_mq_attr *) newattr;
    264 		syscallarg(struct linux_mq_attr *) oldattr;
    265 	} */
    266 	struct linux_mq_attr lattr;
    267 	struct mq_attr attr;
    268 	struct mqueue *mq;
    269 	int error, nonblock;
    270 
    271 	error = copyin(SCARG(uap, newattr), &lattr, sizeof(lattr));
    272 	if (error)
    273 		return error;
    274 	linux_to_bsd_mq_attr(&lattr, &attr);
    275 	nonblock = (attr.mq_flags & O_NONBLOCK);
    276 
    277 	error = mqueue_get((mqd_t)SCARG(uap, mqdes), 0, &mq);
    278 	if (error)
    279 		return error;
    280 
    281 	/* Copy the old attributes, if needed */
    282 	if (SCARG(uap, oldattr)) {
    283 		memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr));
    284 		bsd_to_linux_mq_attr(&attr, &lattr);
    285 	}
    286 
    287 	/* Ignore everything, except O_NONBLOCK */
    288 	if (nonblock)
    289 		mq->mq_attrib.mq_flags |= O_NONBLOCK;
    290 	else
    291 		mq->mq_attrib.mq_flags &= ~O_NONBLOCK;
    292 
    293 	mutex_exit(&mq->mq_mtx);
    294 	fd_putfile((int)SCARG(uap, mqdes));
    295 
    296 	/* Copy the data to the user-space. */
    297 	if (SCARG(uap, oldattr))
    298 		return copyout(&lattr, SCARG(uap, oldattr), sizeof(lattr));
    299 
    300 	return 0;
    301 }
    302 
    303 int
    304 linux_sys_mq_getsetattr(struct lwp *l,
    305     const struct linux_sys_mq_getsetattr_args *uap, register_t *retval)
    306 {
    307 	/* {
    308 		syscallarg(linux_mqd_t) mqdes;
    309 		syscallarg(const struct linux_mq_attr *) newattr;
    310 		syscallarg(struct linux_mq_attr *) oldattr;
    311 	} */
    312 	if (SCARG(uap, newattr) == NULL)
    313 		return linux_sys_mq_getattr(l, uap, retval);
    314 	return linux_sys_mq_setattr(l, uap, retval);
    315 }
    316