1/* $NetBSD */ 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.1 2024/07/01 01:35:53 christos 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 51static void 52linux_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 62static void 63bsd_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 */ 74int 75linux_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 102int 103linux_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 */ 117int 118linux_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), <s, sizeof(lts)); 135 if (error) 136 return error; 137 linux_to_native_timespec(&ts, <s); 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 */ 148int 149linux_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), <s, sizeof(lts)); 167 if (error) 168 return error; 169 linux_to_native_timespec(&ts, <s); 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 */ 184int 185linux_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 */ 231static int 232linux_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 */ 257static int 258linux_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 303int 304linux_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