Before safecopies, the IO_ENDPT and DL_ENDPT message fields were needed to know which actual process to copy data from/to, as that process may not always be the caller. Now that we have full safecopy support, these fields have become useless for that purpose: the owner of the grant is *always* the caller. Allowing the caller to supply another endpoint is in fact dangerous, because the callee may then end up using a grant from a third party. One could call this a variant of the confused deputy problem. From now on, safecopy calls should always use the caller's endpoint as grant owner. This fully obsoletes the DL_ENDPT field in the inet/ethernet protocol. IO_ENDPT has other uses besides identifying the grant owner though. This patch renames IO_ENDPT to USER_ENDPT, not only because that is a more fitting name (it should never be used for I/O after all), but also in order to intentionally break any old system source code outside the base system. If this patch breaks your code, fixing it is fairly simple: - DL_ENDPT should be replaced with m_source; - IO_ENDPT should be replaced with m_source when used for safecopies; - IO_ENDPT should be replaced with USER_ENDPT for any other use, e.g. when setting REP_ENDPT, matching requests in CANCEL calls, getting DEV_SELECT flags, and retrieving of the real user process's endpoint in DEV_OPEN. The changes in this patch are binary backward compatible.
1210 lines
29 KiB
C
1210 lines
29 KiB
C
/*
|
|
* Unix Domain Sockets Implementation (PF_UNIX, PF_LOCAL)
|
|
* This code handles requests generated by operations on /dev/uds
|
|
*
|
|
* The entry points into this file are...
|
|
*
|
|
* uds_open: handles the open(2) syscall on /dev/uds
|
|
* uds_close: handles the close(2) syscall on /dev/uds
|
|
* uds_select: handles the select(2) syscall on /dev/uds
|
|
* uds_read: handles the read(2) syscall on /dev/uds
|
|
* uds_write: handles the write(2) syscall on /dev/uds
|
|
* uds_ioctl: handles the ioctl(2) syscall on /dev/uds
|
|
* uds_status: handles status requests.
|
|
* uds_cancel: handles cancelled syscalls.
|
|
*
|
|
* Also See...
|
|
*
|
|
* table.c, uds.c, uds.h
|
|
*
|
|
* Overview
|
|
*
|
|
* The interface to unix domain sockets is similar to the
|
|
* the interface to network sockets. There is a character
|
|
* device (/dev/uds) that uses STYLE_CLONE and this server
|
|
* is a 'driver' for that device.
|
|
*/
|
|
|
|
#define DEBUG 0
|
|
|
|
#include "inc.h"
|
|
#include "const.h"
|
|
#include "glo.h"
|
|
#include "uds.h"
|
|
|
|
FORWARD _PROTOTYPE( int uds_perform_read, (int minor, endpoint_t m_source,
|
|
size_t size, int pretend));
|
|
FORWARD _PROTOTYPE( int uds_perform_write, (int minor, endpoint_t m_source,
|
|
size_t size, int pretend));
|
|
|
|
PUBLIC int uds_open(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
message fs_m_in, fs_m_out;
|
|
struct ucred ucred;
|
|
int rc, i;
|
|
int minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_open() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
|
|
#endif
|
|
|
|
/*
|
|
* Find a slot in the descriptor table for the new descriptor.
|
|
* The index of the descriptor in the table will be returned.
|
|
* Subsequent calls to read/write/close/ioctl/etc will use this
|
|
* minor number. The minor number must be different from the
|
|
* the /dev/uds device's minor number (currently 0).
|
|
*/
|
|
|
|
minor = -1; /* to trap error */
|
|
|
|
for (i = 1; i < NR_FDS; i++) {
|
|
if (uds_fd_table[i].state == UDS_FREE) {
|
|
minor = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (minor == -1) {
|
|
|
|
/* descriptor table full */
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, ENFILE);
|
|
return ENFILE;
|
|
}
|
|
|
|
/*
|
|
* We found a slot in uds_fd_table, now initialize the descriptor
|
|
*/
|
|
|
|
/* mark this one as 'in use' so that it doesn't get assigned to
|
|
* another socket
|
|
*/
|
|
uds_fd_table[minor].state = UDS_INUSE;
|
|
|
|
/* track the system call we are performing in case it gets cancelled */
|
|
uds_fd_table[minor].call_nr = dev_m_in->m_type;
|
|
uds_fd_table[minor].ioctl = 0;
|
|
uds_fd_table[minor].syscall_done = 0;
|
|
|
|
/* set the socket owner */
|
|
uds_fd_table[minor].owner = dev_m_in->USER_ENDPT;
|
|
uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 0;
|
|
uds_fd_table[minor].select_proc = 0;
|
|
uds_fd_table[minor].sel_ops_in = 0;
|
|
uds_fd_table[minor].sel_ops_out = 0;
|
|
uds_fd_table[minor].status_updated = 0;
|
|
|
|
/* initialize the data pointer (pos) to the start of the PIPE */
|
|
uds_fd_table[minor].pos = 0;
|
|
|
|
/* the PIPE is initially empty */
|
|
uds_fd_table[minor].size = 0;
|
|
|
|
/* the default for a new socket is to allow reading and writing.
|
|
* shutdown(2) will remove one or both flags.
|
|
*/
|
|
uds_fd_table[minor].mode = S_IRUSR|S_IWUSR;
|
|
|
|
/* In libc socket(2) sets this to the actual value later with the
|
|
* NWIOSUDSTYPE ioctl().
|
|
*/
|
|
uds_fd_table[minor].type = -1;
|
|
|
|
/* Clear the backlog by setting each entry to -1 */
|
|
for (i = 0; i < UDS_SOMAXCONN; i++) {
|
|
/* initially no connections are pending */
|
|
uds_fd_table[minor].backlog[i] = -1;
|
|
}
|
|
|
|
memset(&uds_fd_table[minor].ancillary_data, '\0', sizeof(struct
|
|
ancillary));
|
|
for (i = 0; i < OPEN_MAX; i++) {
|
|
uds_fd_table[minor].ancillary_data.fds[i] = -1;
|
|
}
|
|
|
|
/* default the size to UDS_SOMAXCONN */
|
|
uds_fd_table[minor].backlog_size = UDS_SOMAXCONN;
|
|
|
|
/* the socket isn't listening for incoming connections until
|
|
* listen(2) is called
|
|
*/
|
|
uds_fd_table[minor].listening = 0;
|
|
|
|
/* initially the socket is not connected to a peer */
|
|
uds_fd_table[minor].peer = -1;
|
|
|
|
/* there isn't a child waiting to be accept(2)'d */
|
|
uds_fd_table[minor].child = -1;
|
|
|
|
/* initially the socket is not bound or listening on an address */
|
|
memset(&(uds_fd_table[minor].addr), '\0', sizeof(struct sockaddr_un));
|
|
memset(&(uds_fd_table[minor].source), '\0', sizeof(struct sockaddr_un));
|
|
memset(&(uds_fd_table[minor].target), '\0', sizeof(struct sockaddr_un));
|
|
|
|
/* Initially the socket isn't suspended. */
|
|
uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED;
|
|
|
|
/* and the socket doesn't have an I/O grant initially */
|
|
uds_fd_table[minor].io_gr = (cp_grant_id_t) 0;
|
|
|
|
/* since there is no I/O grant it effectively has no size either */
|
|
uds_fd_table[minor].io_gr_size = 0;
|
|
|
|
/* The process isn't suspended so we don't flag it as revivable */
|
|
uds_fd_table[minor].ready_to_revive = 0;
|
|
|
|
/* get the effective user id and effective group id from the endpoint */
|
|
/* this is needed in the REQ_NEWNODE request to PFS. */
|
|
rc = getnucred(uds_fd_table[minor].endpoint, &ucred);
|
|
if (rc == -1) {
|
|
/* roll back the changes we made to the descriptor */
|
|
memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
|
|
|
|
/* likely error: invalid endpoint / proc doesn't exist */
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, errno);
|
|
return errno;
|
|
}
|
|
|
|
/* Prepare Request to the FS side of PFS */
|
|
|
|
fs_m_in.m_type = REQ_NEWNODE;
|
|
fs_m_in.REQ_MODE = I_NAMED_PIPE;
|
|
fs_m_in.REQ_DEV = NO_DEV;
|
|
fs_m_in.REQ_UID = ucred.uid;
|
|
fs_m_in.REQ_GID = ucred.gid;
|
|
|
|
/* Request a new inode on the pipe file system */
|
|
|
|
rc = fs_newnode(&fs_m_in, &fs_m_out);
|
|
if (rc != OK) {
|
|
/* roll back the changes we made to the descriptor */
|
|
memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
|
|
|
|
/* likely error: get_block() failed */
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
|
|
return rc;
|
|
}
|
|
|
|
/* Process the response */
|
|
|
|
uds_fd_table[minor].inode_nr = fs_m_out.RES_INODE_NR;
|
|
|
|
/* prepare the reply */
|
|
|
|
uds_fd_table[minor].syscall_done = 1;
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, minor);
|
|
return minor;
|
|
}
|
|
|
|
PUBLIC int uds_close(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int minor;
|
|
message fs_m_in, fs_m_out;
|
|
int rc;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_close() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* no need to track the syscall in case of cancellation. close() is
|
|
* atomic and can't be cancelled. no need to update the endpoint here,
|
|
* we won't be needing it to kill the socket
|
|
*/
|
|
|
|
/* if the socket is connected, disconnect it */
|
|
if (uds_fd_table[minor].peer != -1) {
|
|
|
|
/* set peer of this peer to -1 */
|
|
uds_fd_table[uds_fd_table[minor].peer].peer = -1;
|
|
|
|
/* error to pass to peer */
|
|
uds_fd_table[uds_fd_table[minor].peer].err = ECONNRESET;
|
|
|
|
/* if peer was blocked on I/O revive peer */
|
|
if (uds_fd_table[uds_fd_table[minor].peer].suspended) {
|
|
|
|
int peer = uds_fd_table[minor].peer;
|
|
|
|
uds_fd_table[peer].ready_to_revive = 1;
|
|
notify(dev_m_in->m_source);
|
|
}
|
|
}
|
|
|
|
if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
|
|
clear_fds(minor, &(uds_fd_table[minor].ancillary_data));
|
|
}
|
|
|
|
/* Prepare Request to the FS side of PFS */
|
|
|
|
fs_m_in.m_type = REQ_PUTNODE;
|
|
fs_m_in.REQ_INODE_NR = uds_fd_table[minor].inode_nr;
|
|
fs_m_in.REQ_COUNT = 1;
|
|
|
|
/* set the socket back to its original UDS_FREE state */
|
|
memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
|
|
|
|
/* Request the removal of the inode from the pipe file system */
|
|
|
|
rc = fs_putnode(&fs_m_in, &fs_m_out);
|
|
if (rc != OK) {
|
|
perror("fs_putnode");
|
|
/* likely error: get_block() failed */
|
|
return rc;
|
|
}
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, OK);
|
|
return OK;
|
|
}
|
|
|
|
PUBLIC int uds_select(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int i, bytes;
|
|
int minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_select() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 1;
|
|
uds_fd_table[minor].select_proc = dev_m_in->m_source;
|
|
|
|
/* track the system call we are performing in case it gets cancelled */
|
|
uds_fd_table[minor].call_nr = dev_m_in->m_type;
|
|
uds_fd_table[minor].ioctl = 0;
|
|
uds_fd_table[minor].syscall_done = 0;
|
|
|
|
/* Can't update the process endpoint here, no info. */
|
|
|
|
uds_fd_table[minor].sel_ops_in = dev_m_in->USER_ENDPT;
|
|
uds_fd_table[minor].sel_ops_out = 0;
|
|
|
|
/* check if there is data available to read */
|
|
bytes = uds_perform_read(minor, dev_m_in->m_source, 1, 1);
|
|
if (bytes > 0) {
|
|
|
|
/* there is data in the pipe for us to read */
|
|
uds_fd_table[minor].sel_ops_out |= SEL_RD;
|
|
|
|
} else if (uds_fd_table[minor].listening == 1) {
|
|
|
|
/* check for pending connections */
|
|
for (i = 0; i < uds_fd_table[minor].backlog_size; i++) {
|
|
if (uds_fd_table[minor].backlog[i] != -1) {
|
|
uds_fd_table[minor].sel_ops_out |= SEL_RD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if we can write without blocking */
|
|
bytes = uds_perform_write(minor, dev_m_in->m_source, PIPE_BUF, 1);
|
|
if (bytes > 0) {
|
|
uds_fd_table[minor].sel_ops_out |= SEL_WR;
|
|
}
|
|
|
|
uds_fd_table[minor].syscall_done = 1;
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT,
|
|
uds_fd_table[minor].sel_ops_out);
|
|
|
|
return uds_fd_table[minor].sel_ops_out;
|
|
}
|
|
|
|
PRIVATE int uds_perform_read(int minor, endpoint_t m_source,
|
|
size_t size, int pretend)
|
|
{
|
|
int rc;
|
|
message fs_m_in;
|
|
message fs_m_out;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_perform_read() call_count=%d\n", minor,
|
|
++call_count);
|
|
#endif
|
|
|
|
/* skip reads and writes of 0 (or less!) bytes */
|
|
if (size <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* check if we are allowed to read */
|
|
if (!(uds_fd_table[minor].mode & S_IRUSR)) {
|
|
|
|
/* socket is shutdown for reading */
|
|
return EPIPE;
|
|
}
|
|
|
|
if (uds_fd_table[minor].size == 0) {
|
|
|
|
if (pretend) {
|
|
return SUSPEND;
|
|
}
|
|
|
|
/* maybe a process is blocked waiting to write? if
|
|
* needed revive the writer
|
|
*/
|
|
if (uds_fd_table[minor].peer != -1 &&
|
|
uds_fd_table[uds_fd_table[minor].peer].suspended) {
|
|
|
|
int peer = uds_fd_table[minor].peer;
|
|
|
|
uds_fd_table[peer].ready_to_revive = 1;
|
|
notify(m_source);
|
|
}
|
|
|
|
#if DEBUG == 1
|
|
printf("(uds) [%d] suspending read request\n", minor);
|
|
#endif
|
|
|
|
/* Process is reading from an empty pipe,
|
|
* suspend it so some bytes can be written
|
|
*/
|
|
uds_fd_table[minor].suspended = UDS_SUSPENDED_READ;
|
|
return SUSPEND;
|
|
}
|
|
|
|
if (pretend) {
|
|
|
|
return (size > uds_fd_table[minor].size) ?
|
|
uds_fd_table[minor].size : size;
|
|
}
|
|
|
|
|
|
/* Prepare Request to the FS side of PFS */
|
|
fs_m_in.m_type = REQ_READ;
|
|
fs_m_in.REQ_INODE_NR = uds_fd_table[minor].inode_nr;
|
|
fs_m_in.REQ_GRANT = uds_fd_table[minor].io_gr;
|
|
fs_m_in.REQ_SEEK_POS_HI = 0;
|
|
fs_m_in.REQ_SEEK_POS_LO = uds_fd_table[minor].pos;
|
|
fs_m_in.REQ_NBYTES = (size > uds_fd_table[minor].size) ?
|
|
uds_fd_table[minor].size : size;
|
|
|
|
/* perform the read */
|
|
rc = fs_readwrite(&fs_m_in, &fs_m_out);
|
|
if (rc != OK) {
|
|
perror("fs_readwrite");
|
|
return rc;
|
|
}
|
|
|
|
/* Process the response */
|
|
#if DEBUG == 1
|
|
printf("(uds) [%d] read complete\n", minor);
|
|
#endif
|
|
|
|
/* move the position of the data pointer up to data we haven't
|
|
* read yet
|
|
*/
|
|
uds_fd_table[minor].pos += fs_m_out.RES_NBYTES;
|
|
|
|
/* decrease the number of unread bytes */
|
|
uds_fd_table[minor].size -= fs_m_out.RES_NBYTES;
|
|
|
|
/* if we have 0 unread bytes, move the data pointer back to the
|
|
* start of the buffer
|
|
*/
|
|
if (uds_fd_table[minor].size == 0) {
|
|
uds_fd_table[minor].pos = 0;
|
|
}
|
|
|
|
/* maybe a big write was waiting for us to read some data, if
|
|
* needed revive the writer
|
|
*/
|
|
if (uds_fd_table[minor].peer != -1 &&
|
|
uds_fd_table[uds_fd_table[minor].peer].suspended) {
|
|
|
|
uds_fd_table[uds_fd_table[minor].peer].ready_to_revive = 1;
|
|
notify(m_source);
|
|
}
|
|
|
|
/* see if peer is blocked on select() and a write is possible
|
|
* (from peer to minor)
|
|
*/
|
|
if (uds_fd_table[minor].peer != -1 &&
|
|
uds_fd_table[uds_fd_table[minor].peer].selecting == 1 &&
|
|
(uds_fd_table[minor].size + uds_fd_table[minor].pos + 1
|
|
< PIPE_BUF)) {
|
|
|
|
int peer = uds_fd_table[minor].peer;
|
|
|
|
/* if the peer wants to know about write being possible
|
|
* and it doesn't know about it already, then let the peer know.
|
|
*/
|
|
if ((uds_fd_table[peer].sel_ops_in & SEL_WR) &&
|
|
!(uds_fd_table[peer].sel_ops_out & SEL_WR)) {
|
|
|
|
/* a write on peer is possible now */
|
|
uds_fd_table[peer].sel_ops_out |= SEL_WR;
|
|
uds_fd_table[peer].status_updated = 1;
|
|
notify(uds_fd_table[peer].select_proc);
|
|
}
|
|
}
|
|
|
|
return fs_m_out.RES_NBYTES; /* return number of bytes read */
|
|
}
|
|
|
|
PRIVATE int uds_perform_write(int minor, endpoint_t m_source,
|
|
size_t size, int pretend)
|
|
{
|
|
int rc, peer, i;
|
|
message fs_m_in;
|
|
message fs_m_out;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_perform_write() call_count=%d\n", minor,
|
|
++call_count);
|
|
#endif
|
|
|
|
/* skip reads and writes of 0 (or less!) bytes */
|
|
if (size <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* check if we are allowed to write */
|
|
if (!(uds_fd_table[minor].mode & S_IWUSR)) {
|
|
|
|
/* socket is shutdown for writing */
|
|
return EPIPE;
|
|
}
|
|
|
|
if (size > PIPE_BUF) {
|
|
|
|
/* message is too big to ever write to the PIPE */
|
|
return EMSGSIZE;
|
|
}
|
|
|
|
if (uds_fd_table[minor].type == SOCK_STREAM ||
|
|
uds_fd_table[minor].type == SOCK_SEQPACKET) {
|
|
|
|
/* if we're writing with a connection oriented socket,
|
|
* then it needs a peer to write to
|
|
*/
|
|
if (uds_fd_table[minor].peer == -1) {
|
|
if (uds_fd_table[minor].err == ECONNRESET) {
|
|
|
|
uds_fd_table[minor].err = 0;
|
|
return ECONNRESET;
|
|
} else {
|
|
return ENOTCONN;
|
|
}
|
|
} else {
|
|
|
|
peer = uds_fd_table[minor].peer;
|
|
}
|
|
|
|
} else /* uds_fd_table[minor].type == SOCK_DGRAM */ {
|
|
|
|
peer = -1;
|
|
|
|
/* locate the "peer" we want to write to */
|
|
for (i = 0; i < NR_FDS; i++) {
|
|
|
|
/* look for a SOCK_DGRAM socket that is bound on
|
|
* the target address
|
|
*/
|
|
if (uds_fd_table[i].type == SOCK_DGRAM &&
|
|
uds_fd_table[i].addr.sun_family == AF_UNIX &&
|
|
!strncmp(uds_fd_table[minor].target.sun_path,
|
|
uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)) {
|
|
|
|
peer = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (peer == -1) {
|
|
return ENOENT;
|
|
}
|
|
}
|
|
|
|
/* check if write would overrun buffer. check if message
|
|
* boundry preserving types (SEQPACKET and DGRAM) wouldn't write
|
|
* to an empty buffer. check if connectionless sockets have a
|
|
* target to write to.
|
|
*/
|
|
if ((uds_fd_table[peer].pos+uds_fd_table[peer].size+size > PIPE_BUF) ||
|
|
((uds_fd_table[minor].type == SOCK_SEQPACKET ||
|
|
uds_fd_table[minor].type == SOCK_DGRAM) &&
|
|
uds_fd_table[peer].size > 0) || (peer == -1)) {
|
|
|
|
if (pretend) {
|
|
return SUSPEND;
|
|
}
|
|
|
|
/* if needed revive the reader */
|
|
if (uds_fd_table[peer].suspended) {
|
|
|
|
uds_fd_table[peer].ready_to_revive = 1;
|
|
notify(m_source);
|
|
}
|
|
|
|
#if DEBUG == 1
|
|
printf("(uds) [%d] suspending write request\n", minor);
|
|
#endif
|
|
|
|
/* Process is reading from an empty pipe,
|
|
* suspend it so some bytes can be written
|
|
*/
|
|
uds_fd_table[minor].suspended = UDS_SUSPENDED_WRITE;
|
|
return SUSPEND;
|
|
}
|
|
|
|
if (pretend) {
|
|
return size;
|
|
}
|
|
|
|
/* Prepare Request to the FS side of PFS */
|
|
fs_m_in.m_type = REQ_WRITE;
|
|
fs_m_in.REQ_INODE_NR = uds_fd_table[peer].inode_nr;
|
|
fs_m_in.REQ_GRANT = uds_fd_table[minor].io_gr;
|
|
fs_m_in.REQ_SEEK_POS_HI = 0;
|
|
fs_m_in.REQ_SEEK_POS_LO = uds_fd_table[peer].pos +
|
|
uds_fd_table[peer].size;
|
|
fs_m_in.REQ_NBYTES = size;
|
|
|
|
/* Request the write */
|
|
rc = fs_readwrite(&fs_m_in, &fs_m_out);
|
|
if (rc != OK) {
|
|
perror("fs_readwrite");
|
|
return rc;
|
|
}
|
|
|
|
/* Process the response */
|
|
#if DEBUG == 1
|
|
printf("(uds) [%d] write complete\n", minor);
|
|
#endif
|
|
/* increase the count of unread bytes */
|
|
uds_fd_table[peer].size += fs_m_out.RES_NBYTES;
|
|
|
|
|
|
/* fill in the source address to be returned by recvfrom & recvmsg */
|
|
if (uds_fd_table[minor].type == SOCK_DGRAM) {
|
|
memcpy(&uds_fd_table[peer].source, &uds_fd_table[minor].addr,
|
|
sizeof(struct sockaddr_un));
|
|
}
|
|
|
|
/* revive peer that was waiting for us to write */
|
|
if (uds_fd_table[peer].suspended) {
|
|
|
|
uds_fd_table[peer].ready_to_revive = 1;
|
|
notify(m_source);
|
|
}
|
|
|
|
/* see if peer is blocked on select()*/
|
|
if (uds_fd_table[peer].selecting == 1 && fs_m_out.RES_NBYTES > 0) {
|
|
|
|
/* if the peer wants to know about data ready to read
|
|
* and it doesn't know about it already, then let the peer
|
|
* know we have data for it.
|
|
*/
|
|
if ((uds_fd_table[peer].sel_ops_in & SEL_RD) &&
|
|
!(uds_fd_table[peer].sel_ops_out & SEL_RD)) {
|
|
|
|
/* a read on peer is possible now */
|
|
uds_fd_table[peer].sel_ops_out |= SEL_RD;
|
|
uds_fd_table[peer].status_updated = 1;
|
|
notify(uds_fd_table[peer].select_proc);
|
|
}
|
|
}
|
|
|
|
return fs_m_out.RES_NBYTES; /* return number of bytes written */
|
|
}
|
|
|
|
PUBLIC int uds_read(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int bytes;
|
|
int minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_read() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
|
|
dev_m_in->POSITION);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
/* track the system call we are performing in case it gets cancelled */
|
|
uds_fd_table[minor].call_nr = dev_m_in->m_type;
|
|
uds_fd_table[minor].ioctl = 0;
|
|
uds_fd_table[minor].syscall_done = 0;
|
|
|
|
/* Update the process endpoint. */
|
|
uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 0;
|
|
|
|
/* save I/O Grant info */
|
|
uds_fd_table[minor].io_gr = (cp_grant_id_t) dev_m_in->IO_GRANT;
|
|
uds_fd_table[minor].io_gr_size = dev_m_in->COUNT;
|
|
|
|
bytes = uds_perform_read(minor, dev_m_in->m_source,
|
|
uds_fd_table[minor].io_gr_size, 0);
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY,
|
|
uds_fd_table[minor].endpoint,
|
|
uds_fd_table[minor].io_gr,
|
|
bytes);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
PUBLIC int uds_write(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int bytes;
|
|
int minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_write() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
|
|
dev_m_in->POSITION);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
/* track the system call we are performing in case it gets cancelled */
|
|
uds_fd_table[minor].call_nr = dev_m_in->m_type;
|
|
uds_fd_table[minor].ioctl = 0;
|
|
uds_fd_table[minor].syscall_done = 0;
|
|
|
|
/* Update the process endpoint. */
|
|
uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 0;
|
|
|
|
/* save I/O Grant info */
|
|
uds_fd_table[minor].io_gr = (cp_grant_id_t) dev_m_in->IO_GRANT;
|
|
uds_fd_table[minor].io_gr_size = dev_m_in->COUNT;
|
|
|
|
bytes = uds_perform_write(minor, dev_m_in->m_source,
|
|
uds_fd_table[minor].io_gr_size, 0);
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY,
|
|
uds_fd_table[minor].endpoint,
|
|
uds_fd_table[minor].io_gr,
|
|
bytes);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int rc, minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_ioctl() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
|
|
dev_m_in->POSITION);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
/* track the system call we are performing in case it gets cancelled */
|
|
uds_fd_table[minor].call_nr = dev_m_in->m_type;
|
|
uds_fd_table[minor].ioctl = dev_m_in->COUNT;
|
|
uds_fd_table[minor].syscall_done = 0;
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 0;
|
|
|
|
/* update the owner endpoint - yes it's really stored in POSITION */
|
|
uds_fd_table[minor].owner = dev_m_in->POSITION;
|
|
|
|
switch (dev_m_in->COUNT) { /* Handle the ioctl(2) command */
|
|
|
|
case NWIOSUDSCONN:
|
|
|
|
/* connect to a listening socket -- connect() */
|
|
rc = do_connect(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSACCEPT:
|
|
|
|
/* accept an incoming connection -- accept() */
|
|
rc = do_accept(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSBLOG:
|
|
|
|
/* set the backlog_size and put the socket into the
|
|
* listening state -- listen()
|
|
*/
|
|
rc = do_listen(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSTYPE:
|
|
|
|
/* set the type for this socket (i.e.
|
|
* SOCK_STREAM, SOCK_DGRAM, etc) -- socket()
|
|
*/
|
|
rc = do_socket(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSADDR:
|
|
|
|
/* set the address for this socket -- bind() */
|
|
rc = do_bind(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSADDR:
|
|
|
|
/* get the address for this socket -- getsockname() */
|
|
rc = do_getsockname(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSPADDR:
|
|
|
|
/* get the address for the peer -- getpeername() */
|
|
rc = do_getpeername(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSSHUT:
|
|
|
|
/* shutdown a socket for reading, writing, or
|
|
* both -- shutdown()
|
|
*/
|
|
rc = do_shutdown(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSPAIR:
|
|
|
|
/* connect two sockets -- socketpair() */
|
|
rc = do_socketpair(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSSOTYPE:
|
|
|
|
/* get socket type -- getsockopt(SO_TYPE) */
|
|
rc = do_getsockopt_sotype(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSPEERCRED:
|
|
|
|
/* get peer endpoint -- getsockopt(SO_PEERCRED) */
|
|
rc = do_getsockopt_peercred(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSTADDR:
|
|
|
|
/* set target address -- sendto() */
|
|
rc = do_sendto(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSFADDR:
|
|
|
|
/* get from address -- recvfrom() */
|
|
rc = do_recvfrom(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSSNDBUF:
|
|
|
|
/* get the send buffer size -- getsockopt(SO_SNDBUF) */
|
|
rc = do_getsockopt_sndbuf(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSSNDBUF:
|
|
|
|
/* set the send buffer size -- setsockopt(SO_SNDBUF) */
|
|
rc = do_setsockopt_sndbuf(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSRCVBUF:
|
|
|
|
/* get the send buffer size -- getsockopt(SO_SNDBUF) */
|
|
rc = do_getsockopt_rcvbuf(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSRCVBUF:
|
|
|
|
/* set the send buffer size -- setsockopt(SO_SNDBUF) */
|
|
rc = do_setsockopt_rcvbuf(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOSUDSCTRL:
|
|
|
|
/* set the control data -- sendmsg() */
|
|
rc = do_sendmsg(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
case NWIOGUDSCTRL:
|
|
|
|
/* set the control data -- recvmsg() */
|
|
rc = do_recvmsg(dev_m_in, dev_m_out);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* the IOCTL command is not valid for /dev/uds --
|
|
* this happens a lot and is normal. a lot of
|
|
* libc functions determine the socket type with
|
|
* IOCTLs. Any not for us simply get a EBADIOCTL
|
|
* response.
|
|
*/
|
|
|
|
rc = EBADIOCTL;
|
|
}
|
|
|
|
if (rc != SUSPEND)
|
|
uds_fd_table[minor].syscall_done = 1;
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
PUBLIC int uds_status(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int i, bytes;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_status() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT, dev_m_in->POSITION);
|
|
#endif
|
|
|
|
for (i = 0; i < NR_FDS; i++) {
|
|
|
|
if (uds_fd_table[i].status_updated == 1) {
|
|
|
|
/* clear the status_updated flag */
|
|
uds_fd_table[i].status_updated = 0;
|
|
uds_fd_table[i].selecting = 0;
|
|
|
|
/* prepare the response */
|
|
dev_m_out->m_type = DEV_IO_READY;
|
|
dev_m_out->DEV_MINOR = i;
|
|
dev_m_out->DEV_SEL_OPS = uds_fd_table[i].sel_ops_out;
|
|
|
|
return uds_fd_table[i].sel_ops_out;
|
|
}
|
|
|
|
if (uds_fd_table[i].ready_to_revive == 1) {
|
|
|
|
/* clear the ready to revive flag */
|
|
uds_fd_table[i].ready_to_revive = 0;
|
|
|
|
switch (uds_fd_table[i].suspended) {
|
|
|
|
case UDS_SUSPENDED_READ:
|
|
|
|
bytes = uds_perform_read(i,
|
|
dev_m_in->m_source,
|
|
uds_fd_table[i].io_gr_size,
|
|
0);
|
|
|
|
if (bytes == SUSPEND) {
|
|
|
|
dev_m_out->m_type =
|
|
DEV_NO_STATUS;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
uds_fd_table[i].suspended =
|
|
UDS_NOT_SUSPENDED;
|
|
|
|
uds_set_reply(dev_m_out,
|
|
DEV_REVIVE,
|
|
uds_fd_table[i].endpoint,
|
|
uds_fd_table[i].io_gr,
|
|
bytes);
|
|
|
|
return bytes;
|
|
|
|
case UDS_SUSPENDED_WRITE:
|
|
|
|
bytes = uds_perform_write(i,
|
|
dev_m_in->m_source,
|
|
uds_fd_table[i].io_gr_size,
|
|
0);
|
|
|
|
if (bytes == SUSPEND) {
|
|
|
|
dev_m_out->m_type =
|
|
DEV_NO_STATUS;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
uds_fd_table[i].suspended =
|
|
UDS_NOT_SUSPENDED;
|
|
|
|
uds_set_reply(dev_m_out,
|
|
DEV_REVIVE,
|
|
uds_fd_table[i].endpoint,
|
|
uds_fd_table[i].io_gr,
|
|
bytes);
|
|
|
|
return bytes;
|
|
|
|
case UDS_SUSPENDED_CONNECT:
|
|
case UDS_SUSPENDED_ACCEPT:
|
|
|
|
/* In both cases, the process
|
|
* that send the notify()
|
|
* already performed the connection.
|
|
* The only thing to do here is
|
|
* unblock.
|
|
*/
|
|
|
|
uds_fd_table[i].suspended =
|
|
UDS_NOT_SUSPENDED;
|
|
|
|
uds_set_reply(dev_m_out,
|
|
DEV_REVIVE,
|
|
uds_fd_table[i].endpoint,
|
|
uds_fd_table[i].io_gr,
|
|
OK);
|
|
|
|
return OK;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
dev_m_out->m_type = DEV_NO_STATUS;
|
|
return OK;
|
|
}
|
|
|
|
PUBLIC int uds_cancel(message *dev_m_in, message *dev_m_out)
|
|
{
|
|
int i, j;
|
|
int minor;
|
|
|
|
#if DEBUG == 1
|
|
static int call_count = 0;
|
|
printf("(uds) [%d] uds_cancel() call_count=%d\n", uds_minor(dev_m_in),
|
|
++call_count);
|
|
printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
|
|
#endif
|
|
|
|
minor = uds_minor(dev_m_in);
|
|
|
|
if (uds_fd_table[minor].state != UDS_INUSE) {
|
|
|
|
/* attempted to close a socket that hasn't been opened --
|
|
* something is very wrong :(
|
|
*/
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Update the process endpoint. */
|
|
uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
|
|
|
|
/* setup select(2) framework */
|
|
uds_fd_table[minor].selecting = 0;
|
|
|
|
/* the system call was cancelled, so if the socket was suspended
|
|
* (which is likely the case), then it is not suspended anymore.
|
|
*/
|
|
uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED;
|
|
|
|
/* If there is a system call and it isn't complete, roll back */
|
|
if (uds_fd_table[minor].call_nr && !uds_fd_table[minor].syscall_done) {
|
|
|
|
|
|
if (uds_fd_table[minor].call_nr == DEV_IOCTL_S) {
|
|
|
|
switch (uds_fd_table[minor].ioctl) {
|
|
|
|
case NWIOSUDSACCEPT: /* accept() */
|
|
|
|
/* partial accept() only changes
|
|
* uds_fd_table[minorparent].child
|
|
*/
|
|
|
|
for (i = 0; i < NR_FDS; i++) {
|
|
if (uds_fd_table[i].child ==
|
|
minor) {
|
|
|
|
uds_fd_table[i].child = -1;
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case NWIOSUDSCONN: /* connect() */
|
|
|
|
/* partial connect() sets addr
|
|
* and adds minor to server backlog
|
|
*/
|
|
|
|
for (i = 0; i < NR_FDS; i++) {
|
|
|
|
/* find a socket that is in
|
|
* use.
|
|
*/
|
|
if (uds_fd_table[i].state ==
|
|
UDS_INUSE) {
|
|
|
|
/* see if minor is in
|
|
* the backlog
|
|
*/
|
|
for (j = 0; j < uds_fd_table[i].backlog_size; j++) {
|
|
|
|
if (uds_fd_table[i].backlog[j] == minor) {
|
|
|
|
/* remove from backlog */
|
|
uds_fd_table[i].backlog[j] = -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* clear the address */
|
|
memset(&(uds_fd_table[minor].addr),
|
|
'\0',
|
|
sizeof(struct sockaddr_un));
|
|
|
|
break;
|
|
|
|
case NWIOSUDSTADDR: /* sendto() */
|
|
case NWIOSUDSADDR: /* bind() */
|
|
case NWIOGUDSADDR: /* getsockname() */
|
|
case NWIOGUDSPADDR: /* getpeername() */
|
|
case NWIOSUDSTYPE: /* socket() */
|
|
case NWIOSUDSBLOG: /* listen() */
|
|
case NWIOSUDSSHUT: /* shutdown() */
|
|
case NWIOSUDSPAIR: /* socketpair() */
|
|
case NWIOGUDSSOTYPE: /* SO_TYPE */
|
|
case NWIOGUDSPEERCRED: /* SO_PEERCRED */
|
|
default:
|
|
/* these are atomic, never suspend,
|
|
* and can't be cancelled once called
|
|
*/
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/* DEV_READ_S or DEV_WRITE_S don't need to do anything
|
|
* when cancelled. DEV_OPEN, DEV_REOPEN, DEV_SELECT,
|
|
* DEV_CLOSE are atomic, never suspend, and can't
|
|
* be cancelled once called.
|
|
*/
|
|
|
|
uds_fd_table[minor].syscall_done = 1;
|
|
}
|
|
|
|
|
|
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT,
|
|
(cp_grant_id_t) dev_m_in->IO_GRANT, EINTR);
|
|
|
|
return EINTR;
|
|
}
|
|
|