kernel: restore setting KTS_NONE
[minix.git] / servers / pfs / uds.c
blob922d2f6684d5589592c5af9d0f3c4d4e815530bd
1 /*
2 * Unix Domain Sockets Implementation (PF_UNIX, PF_LOCAL)
3 * This code handles ioctl(2) commands to implement the socket API.
4 * Some helper functions are also present.
6 * The entry points into this file are...
8 * uds_init: initialize the descriptor table.
9 * do_accept: handles the accept(2) syscall.
10 * do_connect: handles the connect(2) syscall.
11 * do_listen: handles the listen(2) syscall.
12 * do_socket: handles the socket(2) syscall.
13 * do_bind: handles the bind(2) syscall.
14 * do_getsockname: handles the getsockname(2) syscall.
15 * do_getpeername: handles the getpeername(2) syscall.
16 * do_shutdown: handles the shutdown(2) syscall.
17 * do_socketpair: handles the socketpair(2) syscall.
18 * do_getsockopt_sotype: handles the getsockopt(2) syscall.
19 * do_getsockopt_peercred: handles the getsockopt(2) syscall.
20 * do_getsockopt_sndbuf: handles the getsockopt(2) syscall.
21 * do_setsockopt_sndbuf: handles the setsockopt(2) syscall.
22 * do_getsockopt_rcvbuf: handles the getsockopt(2) syscall.
23 * do_setsockopt_rcvbuf: handles the setsockopt(2) syscall.
24 * do_sendto: handles the sendto(2) syscall.
25 * do_recvfrom: handles the recvfrom(2) syscall.
26 * do_sendmsg: handles the sendmsg(2) syscall.
27 * do_recvmsg: handles the recvmsg(2) syscall.
28 * perform_connection: performs the connection of two descriptors.
29 * clear_fds: calls put_filp for undelivered FDs.
31 * Also see...
33 * table.c, dev_uds.c, uds.h
36 #define DEBUG 0
38 #include "inc.h"
39 #include "const.h"
40 #include "glo.h"
41 #include "uds.h"
43 /* File Descriptor Table */
44 uds_fd_t uds_fd_table[NR_FDS];
46 /* initialize the descriptor table */
47 void uds_init(void)
50 * Setting everything to NULL implicitly sets the
51 * state to UDS_FREE.
53 memset(uds_fd_table, '\0', sizeof(uds_fd_t) * NR_FDS);
56 /* check the permissions of a socket file */
57 static int check_perms(int minor, struct sockaddr_un *addr)
59 int rc;
60 message vfs_m;
61 cp_grant_id_t grant_id;
63 grant_id = cpf_grant_direct(VFS_PROC_NR, (vir_bytes) addr->sun_path,
64 UNIX_PATH_MAX, CPF_READ | CPF_WRITE);
66 /* ask the VFS to verify the permissions */
67 memset(&vfs_m, '\0', sizeof(message));
69 vfs_m.m_type = PFS_REQ_CHECK_PERMS;
70 vfs_m.USER_ENDPT = uds_fd_table[minor].owner;
71 vfs_m.IO_GRANT = (char *) grant_id;
72 vfs_m.COUNT = UNIX_PATH_MAX;
74 rc = sendrec(VFS_PROC_NR, &vfs_m);
75 cpf_revoke(grant_id);
76 if (OK != rc) {
77 printf("(uds) sendrec error... req_nr: %d err: %d\n",
78 vfs_m.m_type, rc);
80 return EIO;
83 #if DEBUG == 1
84 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
85 printf("(uds) Canonical Path => %s\n", addr->sun_path);
86 #endif
88 return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
91 static filp_id_t verify_fd(endpoint_t ep, int fd)
93 int rc;
94 message vfs_m;
96 #if DEBUG == 1
97 static int call_count = 0;
98 printf("(uds) verify_fd(%d,%d) call_count=%d\n", ep, fd,
99 ++call_count);
100 #endif
102 memset(&vfs_m, '\0', sizeof(message));
104 vfs_m.m_type = PFS_REQ_VERIFY_FD;
105 vfs_m.USER_ENDPT = ep;
106 vfs_m.COUNT = fd;
108 rc = sendrec(VFS_PROC_NR, &vfs_m);
109 if (OK != rc) {
110 printf("(uds) sendrec error... req_nr: %d err: %d\n",
111 vfs_m.m_type, rc);
112 return NULL;
115 #if DEBUG == 1
116 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
117 #endif
119 return vfs_m.ADDRESS;
122 static int set_filp(filp_id_t sfilp)
124 int rc;
125 message vfs_m;
127 #if DEBUG == 1
128 static int call_count = 0;
129 printf("(uds) set_filp(0x%x) call_count=%d\n", sfilp, ++call_count);
130 #endif
132 memset(&vfs_m, '\0', sizeof(message));
134 vfs_m.m_type = PFS_REQ_SET_FILP;
135 vfs_m.ADDRESS = sfilp;
137 rc = sendrec(VFS_PROC_NR, &vfs_m);
138 if (OK != rc) {
139 printf("(uds) sendrec error... req_nr: %d err: %d\n",
140 vfs_m.m_type, rc);
141 return EIO;
144 #if DEBUG == 1
145 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
146 #endif
147 return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
150 static int copy_filp(endpoint_t to_ep, filp_id_t cfilp)
152 int rc;
153 message vfs_m;
155 #if DEBUG == 1
156 static int call_count = 0;
157 printf("(uds) copy_filp(%d, 0x%x) call_count=%d\n",to_ep, cfilp,
158 ++call_count);
159 #endif
161 memset(&vfs_m, '\0', sizeof(message));
163 vfs_m.m_type = PFS_REQ_COPY_FILP;
164 vfs_m.USER_ENDPT = to_ep;
165 vfs_m.ADDRESS = cfilp;
167 rc = sendrec(VFS_PROC_NR, &vfs_m);
168 if (OK != rc) {
169 printf("(uds) sendrec error... req_nr: %d err: %d\n",
170 vfs_m.m_type, rc);
171 return EIO;
174 #if DEBUG == 1
175 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
176 #endif
177 return vfs_m.m_type;
180 static int put_filp(filp_id_t pfilp)
182 int rc;
183 message vfs_m;
185 #if DEBUG == 1
186 static int call_count = 0;
187 printf("(uds) put_filp(0x%x) call_count=%d\n", pfilp, ++call_count);
188 #endif
190 memset(&vfs_m, '\0', sizeof(message));
192 vfs_m.m_type = PFS_REQ_PUT_FILP;
193 vfs_m.ADDRESS = pfilp;
195 rc = sendrec(VFS_PROC_NR, &vfs_m);
196 if (OK != rc) {
197 printf("(uds) sendrec error... req_nr: %d err: %d\n",
198 vfs_m.m_type, rc);
199 return EIO;
202 #if DEBUG == 1
203 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
204 #endif
205 return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
208 static int cancel_fd(endpoint_t ep, int fd)
210 int rc;
211 message vfs_m;
213 #if DEBUG == 1
214 static int call_count = 0;
215 printf("(uds) cancel_fd(%d,%d) call_count=%d\n", ep, fd, ++call_count);
216 #endif
218 memset(&vfs_m, '\0', sizeof(message));
220 vfs_m.m_type = PFS_REQ_CANCEL_FD;
221 vfs_m.USER_ENDPT = ep;
222 vfs_m.COUNT = fd;
224 rc = sendrec(VFS_PROC_NR, &vfs_m);
225 if (OK != rc) {
226 printf("(uds) sendrec error... req_nr: %d err: %d\n",
227 vfs_m.m_type, rc);
228 return EIO;
231 #if DEBUG == 1
232 printf("(uds) VFS reply => %d\n", vfs_m.m_type);
233 #endif
234 return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
237 int perform_connection(message *dev_m_in, message *dev_m_out,
238 struct sockaddr_un *addr, int minorx, int minory)
240 /* there are several places were a connection is established. */
241 /* accept(2), connect(2), uds_status(2), socketpair(2) */
242 /* This is a helper function to make sure it is done in the */
243 /* same way in each place with the same validation checks. */
245 #if DEBUG == 1
246 static int call_count = 0;
247 printf("(uds) [%d] perform_connection() call_count=%d\n",
248 uds_minor(dev_m_in), ++call_count);
249 #endif
251 /* only connection oriented types are acceptable and only like
252 * types can connect to each other
254 if ((uds_fd_table[minorx].type != SOCK_SEQPACKET &&
255 uds_fd_table[minorx].type != SOCK_STREAM) ||
256 uds_fd_table[minorx].type != uds_fd_table[minory].type) {
258 /* sockets are not in a valid state */
259 return EINVAL;
262 /* connect the pair of sockets */
263 uds_fd_table[minorx].peer = minory;
264 uds_fd_table[minory].peer = minorx;
266 /* Set the address of both sockets */
267 memcpy(&(uds_fd_table[minorx].addr), addr, sizeof(struct sockaddr_un));
268 memcpy(&(uds_fd_table[minory].addr), addr, sizeof(struct sockaddr_un));
270 return OK;
274 int do_accept(message *dev_m_in, message *dev_m_out)
276 int minor;
277 int minorparent; /* minor number of parent (server) */
278 int minorpeer;
279 int rc, i;
280 struct sockaddr_un addr;
282 #if DEBUG == 1
283 static int call_count = 0;
284 printf("(uds) [%d] do_accept() call_count=%d\n",
285 uds_minor(dev_m_in), ++call_count);
286 #endif
288 /* Somewhat weird logic is used in this function, so here's an
289 * overview... The minor number is the server's client socket
290 * (the socket to be returned by accept()). The data waiting
291 * for us in the IO Grant is the address that the server is
292 * listening on. This function uses the address to find the
293 * server's descriptor. From there we can perform the
294 * connection or suspend and wait for a connect().
297 minor = uds_minor(dev_m_in);
299 if (uds_fd_table[minor].type != -1) {
300 /* this IOCTL must be called on a 'fresh' socket */
301 return EINVAL;
304 /* Get the server's address */
305 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
306 (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un));
308 if (rc != OK) {
309 return EIO;
312 /* locate server socket */
313 rc = -1; /* to trap error */
315 for (i = 0; i < NR_FDS; i++) {
317 if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
318 !strncmp(addr.sun_path,
319 uds_fd_table[i].addr.sun_path,
320 UNIX_PATH_MAX) &&
321 uds_fd_table[i].listening == 1) {
323 rc = 0;
324 break;
328 if (rc == -1) {
329 /* there is no server listening on addr. Maybe someone
330 * screwed up the ioctl()?
332 return EINVAL;
335 minorparent = i; /* parent */
337 /* we are the parent's child */
338 uds_fd_table[minorparent].child = minor;
340 /* the peer has the same type as the parent. we need to be that
341 * type too.
343 uds_fd_table[minor].type = uds_fd_table[minorparent].type;
345 /* locate peer to accept in the parent's backlog */
346 minorpeer = -1; /* to trap error */
347 for (i = 0; i < uds_fd_table[minorparent].backlog_size; i++) {
348 if (uds_fd_table[minorparent].backlog[i] != -1) {
349 minorpeer = uds_fd_table[minorparent].backlog[i];
350 uds_fd_table[minorparent].backlog[i] = -1;
351 rc = 0;
352 break;
356 if (minorpeer == -1) {
358 #if DEBUG == 1
359 printf("(uds) [%d] {do_accept} suspend\n", minor);
360 #endif
362 /* there are no peers in the backlog, suspend and wait
363 * for some to show up
365 uds_fd_table[minor].suspended = UDS_SUSPENDED_ACCEPT;
367 return SUSPEND;
370 #if DEBUG == 1
371 printf("(uds) [%d] connecting to %d -- parent is %d\n", minor,
372 minorpeer, minorparent);
373 #endif
375 rc = perform_connection(dev_m_in, dev_m_out, &addr, minor, minorpeer);
376 if (rc != OK) {
377 #if DEBUG == 1
378 printf("(uds) [%d] {do_accept} connection not performed\n",
379 minor);
380 #endif
381 return rc;
384 uds_fd_table[minorparent].child = -1;
386 /* if peer is blocked on connect() revive peer */
387 if (uds_fd_table[minorpeer].suspended) {
388 #if DEBUG == 1
389 printf("(uds) [%d] {do_accept} revive %d\n", minor,
390 minorpeer);
391 #endif
392 uds_fd_table[minorpeer].ready_to_revive = 1;
393 uds_unsuspend(dev_m_in->m_source, minorpeer);
396 return OK;
399 int do_connect(message *dev_m_in, message *dev_m_out)
401 int minor;
402 struct sockaddr_un addr;
403 int rc, i, j;
405 #if DEBUG == 1
406 static int call_count = 0;
407 printf("(uds) [%d] do_connect() call_count=%d\n", uds_minor(dev_m_in),
408 ++call_count);
409 #endif
411 minor = uds_minor(dev_m_in);
413 /* only connection oriented sockets can connect */
414 if (uds_fd_table[minor].type != SOCK_STREAM &&
415 uds_fd_table[minor].type != SOCK_SEQPACKET) {
416 return EINVAL;
419 if (uds_fd_table[minor].peer != -1) {
420 /* socket is already connected */
421 return EISCONN;
424 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
425 (vir_bytes) 0, (vir_bytes) &addr,
426 sizeof(struct sockaddr_un));
428 if (rc != OK) {
429 return EIO;
432 rc = check_perms(minor, &addr);
433 if (rc != OK) {
434 /* permission denied, socket file doesn't exist, etc. */
435 return rc;
438 /* look for a socket of the same type that is listening on the
439 * address we want to connect to
441 for (i = 0; i < NR_FDS; i++) {
443 if (uds_fd_table[minor].type == uds_fd_table[i].type &&
444 uds_fd_table[i].listening &&
445 uds_fd_table[i].addr.sun_family == AF_UNIX &&
446 !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
447 UNIX_PATH_MAX)) {
449 if (uds_fd_table[i].child != -1) {
451 /* the server is blocked on accept(2) --
452 * perform connection to the child
455 rc = perform_connection(dev_m_in, dev_m_out,
456 &addr, minor, uds_fd_table[i].child);
458 if (rc == OK) {
460 uds_fd_table[i].child = -1;
462 #if DEBUG == 1
463 printf("(uds) [%d] {do_connect} revive %d\n", minor, i);
464 #endif
466 /* wake the parent (server) */
467 uds_fd_table[i].ready_to_revive = 1;
468 uds_unsuspend(dev_m_in->m_source, i);
471 return rc;
473 } else {
475 #if DEBUG == 1
476 printf("(uds) [%d] adding to %d's backlog\n",
477 minor, i);
478 #endif
480 /* tell the server were waiting to be served */
482 /* look for a free slot in the backlog */
483 rc = -1; /* to trap error */
484 for (j = 0; j < uds_fd_table[i].backlog_size;
485 j++) {
487 if (uds_fd_table[i].backlog[j] == -1) {
489 uds_fd_table[i].backlog[j] =
490 minor;
492 rc = 0;
493 break;
497 if (rc == -1) {
499 /* backlog is full */
500 break;
503 /* see if the server is blocked on select() */
504 if (uds_fd_table[i].selecting == 1) {
506 /* if the server wants to know
507 * about data ready to read and
508 * it doesn't know about it
509 * already, then let the server
510 * know we have data for it.
512 if ((uds_fd_table[i].sel_ops_in &
513 SEL_RD) &&
514 !(uds_fd_table[i].sel_ops_out &
515 SEL_RD)) {
517 uds_fd_table[i].sel_ops_out |=
518 SEL_RD;
519 uds_fd_table[i].status_updated
520 = 1;
522 uds_unsuspend(
523 dev_m_in->m_source, i);
527 /* we found our server */
528 uds_fd_table[minor].peer = i;
530 /* set the address */
531 memcpy(&(uds_fd_table[minor].addr), &addr,
532 sizeof(struct sockaddr_un));
534 break;
539 if (uds_fd_table[minor].peer == -1) {
540 /* could not find another open socket listening on the
541 * specified address with room in the backlog
543 return ECONNREFUSED;
546 #if DEBUG == 1
547 printf("(uds) [%d] {do_connect} suspend\n", minor);
548 #endif
550 /* suspend until the server side completes the connection with accept()
553 uds_fd_table[minor].suspended = UDS_SUSPENDED_CONNECT;
555 return SUSPEND;
558 int do_listen(message *dev_m_in, message *dev_m_out)
560 int minor;
561 int rc;
562 int backlog_size;
564 #if DEBUG == 1
565 static int call_count = 0;
566 printf("(uds) [%d] do_listen() call_count=%d\n", uds_minor(dev_m_in),
567 ++call_count);
568 #endif
570 minor = uds_minor(dev_m_in);
572 /* ensure the socket has a type and is bound */
573 if (uds_fd_table[minor].type == -1 ||
574 uds_fd_table[minor].addr.sun_family != AF_UNIX) {
576 /* probably trying to call listen() before bind() */
577 return EINVAL;
580 /* the two supported types for listen(2) are SOCK_STREAM and
581 * SOCK_SEQPACKET
583 if (uds_fd_table[minor].type != SOCK_STREAM &&
584 uds_fd_table[minor].type != SOCK_SEQPACKET) {
586 /* probably trying to call listen() with a SOCK_DGRAM */
587 return EOPNOTSUPP;
590 /* The POSIX standard doesn't say what to do if listen() has
591 * already been called. Well, there isn't an errno. we silently
592 * let it happen, but if listen() has already been called, we
593 * don't allow the backlog to shrink
595 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
596 (vir_bytes) 0, (vir_bytes) &backlog_size, sizeof(int));
598 if (rc != OK) {
599 return EIO;
602 if (uds_fd_table[minor].listening == 0) {
604 /* See if backlog_size is between 0 and UDS_SOMAXCONN */
605 if (backlog_size >= 0 && backlog_size < UDS_SOMAXCONN) {
607 /* use the user provided backlog_size */
608 uds_fd_table[minor].backlog_size = backlog_size;
610 } else {
612 /* the user gave an invalid size, use
613 * UDS_SOMAXCONN instead
615 uds_fd_table[minor].backlog_size = UDS_SOMAXCONN;
617 } else {
619 /* See if the user is trying to expand the backlog_size */
620 if (backlog_size > uds_fd_table[minor].backlog_size &&
621 backlog_size < UDS_SOMAXCONN) {
623 /* expand backlog_size */
624 uds_fd_table[minor].backlog_size = backlog_size;
627 /* Don't let the user shrink the backlog_size (we might
628 * have clients waiting in those slots
632 /* perform listen(2) */
633 uds_fd_table[minor].listening = 1;
635 return OK;
638 int do_socket(message *dev_m_in, message *dev_m_out)
640 int rc;
641 int minor;
643 #if DEBUG == 1
644 static int call_count = 0;
645 printf("(uds) [%d] do_socket() call_count=%d\n", uds_minor(dev_m_in),
646 ++call_count);
647 #endif
649 minor = uds_minor(dev_m_in);
651 /* see if this socket already has a type */
652 if (uds_fd_table[minor].type != -1) {
653 /* socket type can only be set once */
654 return EINVAL;
657 /* get the requested type */
658 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
659 (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].type),
660 sizeof(int));
662 if (rc != OK) {
664 /* something went wrong and we couldn't get the type */
665 return EIO;
668 /* validate the type */
669 switch (uds_fd_table[minor].type) {
670 case SOCK_STREAM:
671 case SOCK_DGRAM:
672 case SOCK_SEQPACKET:
674 /* the type is one of the 3 valid socket types */
675 return OK;
677 default:
679 /* if the type isn't one of the 3 valid socket
680 * types, then it must be invalid.
683 /* set the type back to '-1' (no type set) */
684 uds_fd_table[minor].type = -1;
686 return EINVAL;
690 int do_bind(message *dev_m_in, message *dev_m_out)
692 int minor;
693 struct sockaddr_un addr;
694 int rc, i;
696 #if DEBUG == 1
697 static int call_count = 0;
698 printf("(uds) [%d] do_bind() call_count=%d\n", uds_minor(dev_m_in),
699 ++call_count);
700 #endif
702 minor = uds_minor(dev_m_in);
704 if ((uds_fd_table[minor].type == -1) ||
705 (uds_fd_table[minor].addr.sun_family == AF_UNIX &&
706 uds_fd_table[minor].type != SOCK_DGRAM)) {
708 /* the type hasn't been set by do_socket() yet OR attempting
709 * to re-bind() a non-SOCK_DGRAM socket
711 return EINVAL;
714 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
715 (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un));
717 if (rc != OK) {
718 return EIO;
721 /* do some basic sanity checks on the address */
722 if (addr.sun_family != AF_UNIX) {
724 /* bad family */
725 return EAFNOSUPPORT;
728 if (addr.sun_path[0] == '\0') {
730 /* bad address */
731 return ENOENT;
734 rc = check_perms(minor, &addr);
735 if (rc != OK) {
736 /* permission denied, socket file doesn't exist, etc. */
737 return rc;
740 /* make sure the address isn't already in use by another socket. */
741 for (i = 0; i < NR_FDS; i++) {
742 if ((uds_fd_table[i].addr.sun_family == AF_UNIX) &&
743 !strncmp(addr.sun_path,
744 uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)) {
746 /* another socket is bound to this sun_path */
747 return EADDRINUSE;
751 /* looks good, perform the bind() */
752 memcpy(&(uds_fd_table[minor].addr), &addr, sizeof(struct sockaddr_un));
754 return OK;
757 int do_getsockname(message *dev_m_in, message *dev_m_out)
759 int minor;
760 int rc;
762 #if DEBUG == 1
763 static int call_count = 0;
764 printf("(uds) [%d] do_getsockname() call_count=%d\n",
765 uds_minor(dev_m_in), ++call_count);
766 #endif
768 minor = uds_minor(dev_m_in);
770 /* Unconditionally send the address we have assigned to this socket.
771 * The POSIX standard doesn't say what to do if the address
772 * hasn't been set. If the address isn't currently set, then
773 * the user will get NULL bytes. Note: libc depends on this
774 * behavior.
776 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
777 (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].addr),
778 sizeof(struct sockaddr_un));
780 return rc ? EIO : OK;
783 int do_getpeername(message *dev_m_in, message *dev_m_out)
785 int minor;
786 int rc;
788 #if DEBUG == 1
789 static int call_count = 0;
790 printf("(uds) [%d] do_getpeername() call_count=%d\n",
791 uds_minor(dev_m_in), ++call_count);
792 #endif
794 minor = uds_minor(dev_m_in);
796 /* check that the socket is connected with a valid peer */
797 if (uds_fd_table[minor].peer != -1) {
798 int peer_minor;
800 peer_minor = uds_fd_table[minor].peer;
802 /* copy the address from the peer */
803 rc = sys_safecopyto(VFS_PROC_NR,
804 (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0,
805 (vir_bytes) &(uds_fd_table[peer_minor].addr),
806 sizeof(struct sockaddr_un));
808 return rc ? EIO : OK;
809 } else {
810 if (uds_fd_table[minor].err == ECONNRESET) {
811 uds_fd_table[minor].err = 0;
813 return ECONNRESET;
814 } else {
815 return ENOTCONN;
820 int do_shutdown(message *dev_m_in, message *dev_m_out)
822 int minor;
823 int rc, how;
825 #if DEBUG == 1
826 static int call_count = 0;
827 printf("(uds) [%d] do_shutdown() call_count=%d\n",
828 uds_minor(dev_m_in), ++call_count);
829 #endif
831 minor = uds_minor(dev_m_in);
833 if (uds_fd_table[minor].type != SOCK_STREAM &&
834 uds_fd_table[minor].type != SOCK_SEQPACKET) {
836 /* socket must be a connection oriented socket */
837 return EINVAL;
840 if (uds_fd_table[minor].peer == -1) {
841 /* shutdown(2) is only valid for connected sockets */
842 if (uds_fd_table[minor].err == ECONNRESET) {
843 return ECONNRESET;
844 } else {
845 return ENOTCONN;
849 /* get the 'how' parameter from the process */
850 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
851 (vir_bytes) 0, (vir_bytes) &how, sizeof(int));
853 if (rc != OK) {
854 return EIO;
857 switch (how) {
858 case SHUT_RD:
859 /* take away read permission */
860 uds_fd_table[minor].mode =
861 uds_fd_table[minor].mode ^ S_IRUSR;
862 break;
864 case SHUT_WR:
865 /* take away write permission */
866 uds_fd_table[minor].mode =
867 uds_fd_table[minor].mode ^ S_IWUSR;
868 break;
870 case SHUT_RDWR:
871 /* completely shutdown */
872 uds_fd_table[minor].mode = 0;
873 break;
875 default:
876 /* the 'how' parameter is invalid */
877 return EINVAL;
880 return OK;
883 int do_socketpair_old(message *dev_m_in, message *dev_m_out)
885 int rc;
886 short minorin;
887 int minorx, minory;
888 struct sockaddr_un addr;
890 #if DEBUG == 1
891 static int call_count = 0;
892 printf("(uds) [%d] do_socketpair() call_count=%d\n",
893 uds_minor(dev_m_in), ++call_count);
894 #endif
896 /* first ioctl param is the first socket */
897 minorx = uds_minor_old(dev_m_in);
899 /* third ioctl param is the minor number of the second socket */
900 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
901 (vir_bytes) 0, (vir_bytes) &minorin, sizeof(short));
903 if (rc != OK) {
904 return EIO;
907 minory = minor(minorin);
909 #if DEBUG == 1
910 printf("socketpair() %d - %d\n", minorx, minory);
911 #endif
913 /* security check - both sockets must have the same endpoint (owner) */
914 if (uds_fd_table[minorx].owner != uds_fd_table[minory].owner) {
916 /* we won't allow you to magically connect your socket to
917 * someone elses socket
919 return EPERM;
922 addr.sun_family = AF_UNIX;
923 addr.sun_path[0] = 'X';
924 addr.sun_path[1] = '\0';
926 uds_fd_table[minorx].syscall_done = 1;
927 return perform_connection(dev_m_in, dev_m_out, &addr, minorx, minory);
930 int do_socketpair(message *dev_m_in, message *dev_m_out)
932 int rc;
933 dev_t minorin;
934 int minorx, minory;
935 struct sockaddr_un addr;
937 #if DEBUG == 1
938 static int call_count = 0;
939 printf("(uds) [%d] do_socketpair() call_count=%d\n",
940 uds_minor(dev_m_in), ++call_count);
941 #endif
943 /* first ioctl param is the first socket */
944 minorx = uds_minor(dev_m_in);
946 /* third ioctl param is the minor number of the second socket */
947 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
948 (vir_bytes) 0, (vir_bytes) &minorin, sizeof(dev_t));
950 if (rc != OK) {
951 return EIO;
954 minory = minor(minorin);
956 #if DEBUG == 1
957 printf("socketpair() %d - %d\n", minorx, minory);
958 #endif
960 /* security check - both sockets must have the same endpoint (owner) */
961 if (uds_fd_table[minorx].owner != uds_fd_table[minory].owner) {
963 /* we won't allow you to magically connect your socket to
964 * someone elses socket
966 return EPERM;
969 addr.sun_family = AF_UNIX;
970 addr.sun_path[0] = 'X';
971 addr.sun_path[1] = '\0';
973 uds_fd_table[minorx].syscall_done = 1;
974 return perform_connection(dev_m_in, dev_m_out, &addr, minorx, minory);
977 int do_getsockopt_sotype(message *dev_m_in, message *dev_m_out)
979 int minor;
980 int rc;
982 #if DEBUG == 1
983 static int call_count = 0;
984 printf("(uds) [%d] do_getsockopt_sotype() call_count=%d\n",
985 uds_minor(dev_m_in), ++call_count);
986 #endif
988 minor = uds_minor(dev_m_in);
990 if (uds_fd_table[minor].type == -1) {
992 /* the type hasn't been set yet. instead of returning an
993 * invalid type, we fail with EINVAL
995 return EINVAL;
998 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
999 (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].type),
1000 sizeof(int));
1002 return rc ? EIO : OK;
1005 int do_getsockopt_peercred(message *dev_m_in, message *dev_m_out)
1007 int minor;
1008 int peer_minor;
1009 int rc;
1010 struct ucred cred;
1012 #if DEBUG == 1
1013 static int call_count = 0;
1014 printf("(uds) [%d] do_getsockopt_peercred() call_count=%d\n",
1015 uds_minor(dev_m_in), ++call_count);
1016 #endif
1018 minor = uds_minor(dev_m_in);
1020 if (uds_fd_table[minor].peer == -1) {
1022 if (uds_fd_table[minor].err == ECONNRESET) {
1023 uds_fd_table[minor].err = 0;
1025 return ECONNRESET;
1026 } else {
1027 return ENOTCONN;
1031 peer_minor = uds_fd_table[minor].peer;
1033 /* obtain the peer's credentials */
1034 rc = getnucred(uds_fd_table[peer_minor].owner, &cred);
1035 if (rc == -1) {
1036 /* likely error: invalid endpoint / proc doesn't exist */
1037 return errno;
1040 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1041 (vir_bytes) 0, (vir_bytes) &cred, sizeof(struct ucred));
1043 return rc ? EIO : OK;
1046 int do_getsockopt_peercred_old(message *dev_m_in, message *dev_m_out)
1048 int minor;
1049 int peer_minor;
1050 int rc;
1051 struct ucred cred;
1052 struct ucred_old cred_old;
1054 #if DEBUG == 1
1055 static int call_count = 0;
1056 printf("(uds) [%d] do_getsockopt_peercred() call_count=%d\n",
1057 uds_minor(dev_m_in), ++call_count);
1058 #endif
1060 minor = uds_minor(dev_m_in);
1062 if (uds_fd_table[minor].peer == -1) {
1064 if (uds_fd_table[minor].err == ECONNRESET) {
1065 uds_fd_table[minor].err = 0;
1067 return ECONNRESET;
1068 } else {
1069 return ENOTCONN;
1073 peer_minor = uds_fd_table[minor].peer;
1075 /* obtain the peer's credentials */
1076 rc = getnucred(uds_fd_table[peer_minor].owner, &cred);
1077 if (rc == -1) {
1078 /* likely error: invalid endpoint / proc doesn't exist */
1079 return errno;
1082 /* copy to old structure */
1083 cred_old.pid = cred.pid;
1084 cred_old.uid = (short) cred.uid;
1085 cred_old.gid = (char) cred.gid;
1087 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1088 (vir_bytes) 0, (vir_bytes) &cred_old, sizeof(struct ucred_old));
1090 return rc ? EIO : OK;
1093 int do_getsockopt_sndbuf(message *dev_m_in, message *dev_m_out)
1095 int rc;
1096 size_t sndbuf = PIPE_BUF;
1098 #if DEBUG == 1
1099 static int call_count = 0;
1100 printf("(uds) [%d] do_getsockopt_sndbuf() call_count=%d\n",
1101 uds_minor(dev_m_in), ++call_count);
1102 #endif
1104 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1105 (vir_bytes) 0, (vir_bytes) &(sndbuf), sizeof(size_t));
1107 return rc ? EIO : OK;
1110 int do_setsockopt_sndbuf(message *dev_m_in, message *dev_m_out)
1112 int rc;
1113 size_t sndbuf;
1115 #if DEBUG == 1
1116 static int call_count = 0;
1117 printf("(uds) [%d] do_setsockopt_rcvbuf() call_count=%d\n",
1118 uds_minor(dev_m_in), ++call_count);
1119 #endif
1121 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1122 (vir_bytes) 0, (vir_bytes) &sndbuf,
1123 sizeof(size_t));
1125 if (rc != OK) {
1126 return EIO;
1129 if (sndbuf > PIPE_BUF) {
1130 /* The send buffer is limited to 32K at the moment. */
1131 return ENOSYS;
1134 /* There is no way to reduce the send buffer, do we have to
1135 * let this call fail for smaller buffers?
1137 return OK;
1140 int do_getsockopt_rcvbuf(message *dev_m_in, message *dev_m_out)
1142 int rc;
1143 size_t rcvbuf = PIPE_BUF;
1145 #if DEBUG == 1
1146 static int call_count = 0;
1147 printf("(uds) [%d] do_getsockopt_rcvbuf() call_count=%d\n",
1148 uds_minor(dev_m_in), ++call_count);
1149 #endif
1151 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1152 (vir_bytes) 0, (vir_bytes) &(rcvbuf), sizeof(size_t));
1154 return rc ? EIO : OK;
1157 int do_setsockopt_rcvbuf(message *dev_m_in, message *dev_m_out)
1159 int rc;
1160 size_t rcvbuf;
1162 #if DEBUG == 1
1163 static int call_count = 0;
1164 printf("(uds) [%d] do_setsockopt_rcvbuf() call_count=%d\n",
1165 uds_minor(dev_m_in), ++call_count);
1166 #endif
1168 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1169 (vir_bytes) 0, (vir_bytes) &rcvbuf,
1170 sizeof(size_t));
1172 if (rc != OK) {
1173 return EIO;
1176 if (rcvbuf > PIPE_BUF) {
1177 /* The send buffer is limited to 32K at the moment. */
1178 return ENOSYS;
1181 /* There is no way to reduce the send buffer, do we have to
1182 * let this call fail for smaller buffers?
1184 return OK;
1188 int do_sendto(message *dev_m_in, message *dev_m_out)
1190 int minor;
1191 int rc;
1192 struct sockaddr_un addr;
1194 #if DEBUG == 1
1195 static int call_count = 0;
1196 printf("(uds) [%d] do_sendto() call_count=%d\n", uds_minor(dev_m_in),
1197 ++call_count);
1198 #endif
1200 minor = uds_minor(dev_m_in);
1202 if (uds_fd_table[minor].type != SOCK_DGRAM) {
1203 /* This IOCTL is only for SOCK_DGRAM sockets */
1204 return EINVAL;
1207 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1208 (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un));
1210 if (rc != OK) {
1211 return EIO;
1214 /* do some basic sanity checks on the address */
1215 if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0') {
1216 /* bad address */
1217 return EINVAL;
1220 rc = check_perms(minor, &addr);
1221 if (rc != OK) {
1222 return rc;
1225 memcpy(&(uds_fd_table[minor].target), &addr,
1226 sizeof(struct sockaddr_un));
1228 return OK;
1231 int do_recvfrom(message *dev_m_in, message *dev_m_out)
1233 int minor;
1234 int rc;
1236 #if DEBUG == 1
1237 static int call_count = 0;
1238 printf("(uds) [%d] do_recvfrom() call_count=%d\n",
1239 uds_minor(dev_m_in), ++call_count);
1240 #endif
1242 minor = uds_minor(dev_m_in);
1244 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1245 (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].source),
1246 sizeof(struct sockaddr_un));
1248 return rc ? EIO : OK;
1251 int msg_control_read(struct msg_control *msg_ctrl, struct ancillary *data,
1252 int minor)
1254 int rc;
1255 struct msghdr msghdr;
1256 struct cmsghdr *cmsg = NULL;
1258 #if DEBUG == 1
1259 static int call_count = 0;
1260 printf("(uds) [%d] msg_control_read() call_count=%d\n", minor,
1261 ++call_count);
1262 #endif
1264 data->nfiledes = 0;
1266 memset(&msghdr, '\0', sizeof(struct msghdr));
1267 msghdr.msg_control = msg_ctrl->msg_control;
1268 msghdr.msg_controllen = msg_ctrl->msg_controllen;
1270 for(cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
1271 cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
1273 if (cmsg->cmsg_level == SOL_SOCKET &&
1274 cmsg->cmsg_type == SCM_RIGHTS) {
1276 int i;
1277 int nfds =
1278 MIN((cmsg->cmsg_len-CMSG_LEN(0))/sizeof(int),
1279 OPEN_MAX);
1281 for (i = 0; i < nfds; i++) {
1282 if (data->nfiledes == OPEN_MAX) {
1283 return EOVERFLOW;
1286 data->fds[data->nfiledes] =
1287 ((int *) CMSG_DATA(cmsg))[i];
1288 #if DEBUG == 1
1289 printf("(uds) [%d] fd[%d]=%d\n", minor,
1290 data->nfiledes, data->fds[data->nfiledes]);
1291 #endif
1292 data->nfiledes++;
1297 /* obtain this socket's credentials */
1298 rc = getnucred(uds_fd_table[minor].owner, &(data->cred));
1299 if (rc == -1) {
1300 return errno;
1302 #if DEBUG == 1
1303 printf("(uds) [%d] cred={%d,%d,%d}\n", minor,
1304 data->cred.pid, data->cred.uid,
1305 data->cred.gid);
1306 #endif
1307 return OK;
1310 static int send_fds(int minor, struct ancillary *data)
1312 int rc, i, j;
1314 #if DEBUG == 1
1315 static int call_count = 0;
1316 printf("(uds) [%d] send_fds() call_count=%d\n", minor, ++call_count);
1317 #endif
1319 /* verify the file descriptors and get their filps. */
1320 for (i = 0; i < data->nfiledes; i++) {
1321 data->filps[i] = verify_fd(uds_fd_table[minor].owner,
1322 data->fds[i]);
1324 if (data->filps[i] == NULL) {
1325 return EINVAL;
1329 /* set them as in-flight */
1330 for (i = 0; i < data->nfiledes; i++) {
1331 rc = set_filp(data->filps[i]);
1332 if (rc != OK) {
1333 /* revert set_filp() calls */
1334 for (j = i; j >= 0; j--) {
1335 put_filp(data->filps[j]);
1337 return rc;
1341 return OK;
1344 int clear_fds(int minor, struct ancillary *data)
1346 /* This function calls put_filp() for all of the FDs in data.
1347 * This is used when a Unix Domain Socket is closed and there
1348 * exists references to file descriptors that haven't been received
1349 * with recvmsg().
1351 int i;
1353 #if DEBUG == 1
1354 static int call_count = 0;
1355 printf("(uds) [%d] recv_fds() call_count=%d\n", minor,
1356 ++call_count);
1357 #endif
1359 for (i = 0; i < data->nfiledes; i++) {
1360 put_filp(data->filps[i]);
1361 #if DEBUG == 1
1362 printf("(uds) clear_fds() => %d\n", data->fds[i]);
1363 #endif
1364 data->fds[i] = -1;
1365 data->filps[i] = NULL;
1368 data->nfiledes = 0;
1370 return OK;
1373 static int recv_fds(int minor, struct ancillary *data,
1374 struct msg_control *msg_ctrl)
1376 int rc, i, j;
1377 struct msghdr msghdr;
1378 struct cmsghdr *cmsg;
1379 endpoint_t to_ep;
1381 #if DEBUG == 1
1382 static int call_count = 0;
1383 printf("(uds) [%d] recv_fds() call_count=%d\n", minor,
1384 ++call_count);
1385 #endif
1387 msghdr.msg_control = msg_ctrl->msg_control;
1388 msghdr.msg_controllen = msg_ctrl->msg_controllen;
1390 cmsg = CMSG_FIRSTHDR(&msghdr);
1391 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * data->nfiledes);
1392 cmsg->cmsg_level = SOL_SOCKET;
1393 cmsg->cmsg_type = SCM_RIGHTS;
1395 to_ep = uds_fd_table[minor].owner;
1397 /* copy to the target endpoint */
1398 for (i = 0; i < data->nfiledes; i++) {
1399 rc = copy_filp(to_ep, data->filps[i]);
1400 if (rc < 0) {
1401 /* revert set_filp() calls */
1402 for (j = 0; j < data->nfiledes; j++) {
1403 put_filp(data->filps[j]);
1405 /* revert copy_filp() calls */
1406 for (j = i; j >= 0; j--) {
1407 cancel_fd(to_ep, data->fds[j]);
1409 return rc;
1411 data->fds[i] = rc; /* data->fds[i] now has the new FD */
1414 for (i = 0; i < data->nfiledes; i++) {
1415 put_filp(data->filps[i]);
1416 #if DEBUG == 1
1417 printf("(uds) recv_fds() => %d\n", data->fds[i]);
1418 #endif
1419 ((int *)CMSG_DATA(cmsg))[i] = data->fds[i];
1420 data->fds[i] = -1;
1421 data->filps[i] = NULL;
1424 data->nfiledes = 0;
1426 return OK;
1429 static int recv_cred(int minor, struct ancillary *data,
1430 struct msg_control *msg_ctrl)
1432 struct msghdr msghdr;
1433 struct cmsghdr *cmsg;
1435 #if DEBUG == 1
1436 static int call_count = 0;
1437 printf("(uds) [%d] recv_cred() call_count=%d\n", minor,
1438 ++call_count);
1439 #endif
1441 msghdr.msg_control = msg_ctrl->msg_control;
1442 msghdr.msg_controllen = msg_ctrl->msg_controllen;
1444 cmsg = CMSG_FIRSTHDR(&msghdr);
1445 if (cmsg->cmsg_len > 0) {
1446 cmsg = CMSG_NXTHDR(&msghdr, cmsg);
1449 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
1450 cmsg->cmsg_level = SOL_SOCKET;
1451 cmsg->cmsg_type = SCM_CREDENTIALS;
1452 memcpy(CMSG_DATA(cmsg), &(data->cred), sizeof(struct ucred));
1454 return OK;
1457 int do_sendmsg(message *dev_m_in, message *dev_m_out)
1459 int minor, peer, rc, i;
1460 struct msg_control msg_ctrl;
1462 #if DEBUG == 1
1463 static int call_count = 0;
1464 printf("(uds) [%d] do_sendmsg() call_count=%d\n",
1465 uds_minor(dev_m_in), ++call_count);
1466 #endif
1468 minor = uds_minor(dev_m_in);
1470 memset(&msg_ctrl, '\0', sizeof(struct msg_control));
1472 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1473 (vir_bytes) 0, (vir_bytes) &msg_ctrl,
1474 sizeof(struct msg_control));
1476 if (rc != OK) {
1477 return EIO;
1480 /* locate peer */
1481 peer = -1;
1482 if (uds_fd_table[minor].type == SOCK_DGRAM) {
1483 if (uds_fd_table[minor].target.sun_path[0] == '\0' ||
1484 uds_fd_table[minor].target.sun_family != AF_UNIX) {
1486 return EDESTADDRREQ;
1489 for (i = 0; i < NR_FDS; i++) {
1491 /* look for a SOCK_DGRAM socket that is bound on
1492 * the target address
1494 if (uds_fd_table[i].type == SOCK_DGRAM &&
1495 uds_fd_table[i].addr.sun_family == AF_UNIX &&
1496 !strncmp(uds_fd_table[minor].target.sun_path,
1497 uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)){
1499 peer = i;
1500 break;
1504 if (peer == -1) {
1505 return ENOENT;
1507 } else {
1508 peer = uds_fd_table[minor].peer;
1509 if (peer == -1) {
1510 return ENOTCONN;
1514 #if DEBUG == 1
1515 printf("(uds) [%d] sendmsg() -- peer=%d\n", minor, peer);
1516 #endif
1517 /* note: it's possible that there is already some file
1518 * descriptors in ancillary_data if the peer didn't call
1519 * recvmsg() yet. That's okay. The receiver will
1520 * get the current file descriptors plus the new ones.
1522 rc = msg_control_read(&msg_ctrl, &uds_fd_table[peer].ancillary_data,
1523 minor);
1524 if (rc != OK) {
1525 return rc;
1528 return send_fds(minor, &uds_fd_table[peer].ancillary_data);
1531 int do_recvmsg(message *dev_m_in, message *dev_m_out)
1533 int minor;
1534 int rc;
1535 struct msg_control msg_ctrl;
1536 socklen_t controllen_avail = 0;
1537 socklen_t controllen_needed = 0;
1538 socklen_t controllen_desired = 0;
1540 #if DEBUG == 1
1541 static int call_count = 0;
1542 printf("(uds) [%d] do_sendmsg() call_count=%d\n",
1543 uds_minor(dev_m_in), ++call_count);
1544 #endif
1546 minor = uds_minor(dev_m_in);
1549 #if DEBUG == 1
1550 printf("(uds) [%d] CREDENTIALS {pid:%d,uid:%d,gid:%d}\n", minor,
1551 uds_fd_table[minor].ancillary_data.cred.pid,
1552 uds_fd_table[minor].ancillary_data.cred.uid,
1553 uds_fd_table[minor].ancillary_data.cred.gid);
1554 #endif
1556 memset(&msg_ctrl, '\0', sizeof(struct msg_control));
1558 /* get the msg_control from the user, it will include the
1559 * amount of space the user has allocated for control data.
1561 rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1562 (vir_bytes) 0, (vir_bytes) &msg_ctrl,
1563 sizeof(struct msg_control));
1565 if (rc != OK) {
1566 return EIO;
1569 controllen_avail = MIN(msg_ctrl.msg_controllen, MSG_CONTROL_MAX);
1571 if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
1572 controllen_needed = CMSG_LEN(sizeof(int) *
1573 (uds_fd_table[minor].ancillary_data.nfiledes));
1576 /* if there is room we also include credentials */
1577 controllen_desired = controllen_needed +
1578 CMSG_LEN(sizeof(struct ucred));
1580 if (controllen_needed > controllen_avail) {
1581 return EOVERFLOW;
1584 rc = recv_fds(minor, &uds_fd_table[minor].ancillary_data, &msg_ctrl);
1585 if (rc != OK) {
1586 return rc;
1589 if (controllen_desired <= controllen_avail) {
1590 rc = recv_cred(minor, &uds_fd_table[minor].ancillary_data,
1591 &msg_ctrl);
1592 if (rc != OK) {
1593 return rc;
1597 /* send the user the control data */
1598 rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
1599 (vir_bytes) 0, (vir_bytes) &msg_ctrl,
1600 sizeof(struct msg_control));
1602 return rc ? EIO : OK;