aboutsummaryrefslogtreecommitdiff
path: root/pflocal/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'pflocal/io.c')
-rw-r--r--pflocal/io.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/pflocal/io.c b/pflocal/io.c
new file mode 100644
index 00000000..133c75c9
--- /dev/null
+++ b/pflocal/io.c
@@ -0,0 +1,622 @@
+/* Socket I/O operations
+
+ Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h> /* For bzero() */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <hurd.h> /* for getauth() */
+#include <hurd/hurd_types.h>
+#include <hurd/auth.h>
+#include <hurd/pipe.h>
+#include <mach/notify.h>
+
+#include "sock.h"
+#include "connq.h"
+#include "sserver.h"
+
+#include "io_S.h"
+#include "interrupt_S.h"
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in amount. */
+error_t
+S_io_read (struct sock_user *user,
+ char **data, mach_msg_type_number_t *data_len,
+ off_t offset, mach_msg_type_number_t amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *data_len = 0;
+ }
+ else if (!err)
+ {
+ err =
+ pipe_read (pipe, user->sock->flags & SOCK_NONBLOCK, NULL,
+ data, data_len, amount);
+ pipe_release_reader (pipe);
+ }
+
+ return err;
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+S_io_write (struct sock_user *user,
+ char *data, mach_msg_type_number_t data_len,
+ off_t offset, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_write_pipe (user->sock, &pipe);
+ if (!err)
+ {
+ struct addr *source_addr;
+
+ /* We could provide a source address for all writes, but we only do so
+ for connectionless sockets because that's the only place it's
+ required, and it's more efficient not to. */
+ if (pipe->class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_get_addr (user->sock, &source_addr);
+ else
+ source_addr = NULL;
+
+ if (!err)
+ {
+ err = pipe_write (pipe, user->sock->flags & SOCK_NONBLOCK,
+ source_addr, data, data_len, amount);
+ if (source_addr)
+ ports_port_deref (source_addr);
+ }
+
+ pipe_release_writer (pipe);
+ }
+
+ return err;
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+S_io_readable (struct sock_user *user, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *amount = 0;
+ }
+ else if (!err)
+ {
+ *amount = pipe_readable (user->sock->read_pipe, 1);
+ pipe_release_reader (pipe);
+ }
+
+ return err;
+}
+
+/* Change current read/write offset */
+error_t
+S_io_seek (struct sock_user *user,
+ off_t offset, int whence, off_t *new_offset)
+{
+ return user ? ESPIPE : EOPNOTSUPP;
+}
+
+/* Return a new port with the same semantics as the existing port. */
+error_t
+S_io_duplicate (struct sock_user *user,
+ mach_port_t *new_port, mach_msg_type_name_t *new_port_type)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_create_port (user->sock, new_port);
+ if (! err)
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ return err;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. */
+error_t
+S_io_select (struct sock_user *user,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ error_t err = 0;
+ struct sock *sock;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ *select_type &= SELECT_READ | SELECT_WRITE;
+
+ sock = user->sock;
+ mutex_lock (&sock->lock);
+
+ if (sock->listen_queue)
+ /* Sock is used for accepting connections, not I/O. For these, you can
+ only select for reading, which will block until a connection request
+ comes along. */
+ {
+ mutex_unlock (&sock->lock);
+
+ *select_type &= SELECT_READ;
+
+ if (*select_type & SELECT_READ)
+ {
+ /* Wait for a connect. Passing in NULL for REQ means that the
+ request won't be dequeued. */
+ if (connq_listen (sock->listen_queue, 1, NULL, NULL) == 0)
+ /* We can satisfy this request immediately. */
+ return 0;
+ else
+ /* Gotta wait... */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ return connq_listen (sock->listen_queue, 0, NULL, NULL);
+ }
+ }
+ }
+ else
+ /* Sock is a normal read/write socket. */
+ {
+ int valid;
+ int ready = 0;
+ struct pipe *read_pipe = sock->read_pipe;
+ struct pipe *write_pipe = sock->write_pipe;
+
+ if (! write_pipe)
+ ready |= SELECT_WRITE;
+ if (! read_pipe)
+ ready |= SELECT_READ;
+ ready &= *select_type; /* Only keep things requested. */
+ *select_type &= ~ready;
+
+ valid = *select_type;
+ if (valid & SELECT_READ)
+ {
+ pipe_acquire_reader (read_pipe);
+ if (pipe_wait_readable (read_pipe, 1, 1) != EWOULDBLOCK)
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ mutex_unlock (&read_pipe->lock);
+ }
+ if (valid & SELECT_WRITE)
+ {
+ pipe_acquire_writer (write_pipe);
+ if (pipe_wait_writable (write_pipe, 1) != EWOULDBLOCK)
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ mutex_unlock (&write_pipe->lock);
+ }
+
+ mutex_unlock (&sock->lock);
+
+ if (ready)
+ /* No need to block, we've already got some results. */
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ err = pipe_pair_select (read_pipe, write_pipe, select_type, 1);
+ }
+
+ if (valid & SELECT_READ)
+ pipe_remove_reader (read_pipe);
+ if (valid & SELECT_WRITE)
+ pipe_remove_writer (write_pipe);
+ }
+
+ return err;
+}
+
+/* Return the current status of the object. Not all the fields of the
+ io_statuf_t are meaningful for all objects; however, the access and
+ modify times, the optimal IO size, and the fs type are meaningful
+ for all objects. */
+error_t
+S_io_stat (struct sock_user *user, struct stat *st)
+{
+ struct sock *sock;
+ struct pipe *rpipe, *wpipe;
+
+ void copy_time (time_value_t *from, time_t *to_sec, unsigned long *to_usec)
+ {
+ *to_sec = from->seconds;
+ *to_usec = from->microseconds;
+ }
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ bzero (st, sizeof (struct stat));
+
+ st->st_fstype = FSTYPE_SOCKET;
+ st->st_mode = S_IFSOCK;
+ st->st_fsid = getpid ();
+ st->st_ino = sock->id;
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+
+ mutex_lock (&sock->lock); /* Make sure the pipes don't go away... */
+
+ rpipe = sock->read_pipe;
+ wpipe = sock->write_pipe;
+
+ if (rpipe)
+ {
+ mutex_lock (&rpipe->lock);
+ copy_time (&rpipe->read_time, &st->st_atime, &st->st_atime_usec);
+ /* This seems useful. */
+ st->st_size = pipe_readable (rpipe, 1);
+ mutex_unlock (&rpipe->lock);
+ }
+
+ if (wpipe)
+ {
+ mutex_lock (&wpipe->lock);
+ copy_time (&wpipe->write_time, &st->st_mtime, &st->st_mtime_usec);
+ mutex_unlock (&wpipe->lock);
+ }
+
+ copy_time (&sock->change_time, &st->st_ctime, &st->st_ctime_usec);
+
+ mutex_unlock (&sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_get_openmodes (struct sock_user *user, int *bits)
+{
+ unsigned flags;
+ if (!user)
+ return EOPNOTSUPP;
+ flags = user->sock->flags;
+ *bits =
+ O_APPEND /* pipes always append */
+ | (flags & SOCK_NONBLOCK ? O_NONBLOCK : 0)
+ | (flags & SOCK_SHUTDOWN_READ ? 0 : O_READ)
+ | (flags & SOCK_SHUTDOWN_WRITE ? 0 : O_WRITE);
+ return 0;
+}
+
+error_t
+S_io_set_all_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= SOCK_NONBLOCK;
+ else
+ user->sock->flags &= ~SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_set_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_clear_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags &= ~SOCK_NONBLOCK;
+ mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+#define NIDS 10
+
+error_t
+S_io_reauthenticate (struct sock_user *user, mach_port_t rendezvous)
+{
+ error_t err;
+ mach_port_t auth_server;
+ mach_port_t new_user_port;
+ uid_t uids_buf[NIDS], aux_uids_buf[NIDS];
+ uid_t *uids = uids_buf, *aux_uids = aux_uids_buf;
+ gid_t gids_buf[NIDS], aux_gids_buf[NIDS];
+ gid_t *gids = gids_buf, *aux_gids = aux_gids_buf;
+ unsigned num_uids = NIDS, num_aux_uids = NIDS;
+ unsigned num_gids = NIDS, num_aux_gids = NIDS;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ do
+ err = sock_create_port (user->sock, &new_user_port);
+ while (err == EINTR);
+ if (err)
+ return err;
+
+ auth_server = getauth ();
+ err = mach_port_insert_right (mach_task_self (), new_user_port,
+ new_user_port, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ do
+ err =
+ auth_server_authenticate (auth_server,
+ rendezvous, MACH_MSG_TYPE_COPY_SEND,
+ new_user_port, MACH_MSG_TYPE_COPY_SEND,
+ &uids, &num_uids, &aux_uids, &num_aux_uids,
+ &gids, &num_gids, &aux_gids, &num_aux_gids);
+ while (err == EINTR);
+ mach_port_deallocate (mach_task_self (), rendezvous);
+ mach_port_deallocate (mach_task_self (), auth_server);
+ mach_port_deallocate (mach_task_self (), new_user_port);
+
+ /* Throw away the ids we went through all that trouble to get... */
+#define TRASH_IDS(ids, buf, num) \
+ if (buf != ids) \
+ munmap (ids, num * sizeof (uid_t));
+
+ TRASH_IDS (uids, uids_buf, num_uids);
+ TRASH_IDS (gids, gids_buf, num_gids);
+ TRASH_IDS (aux_uids, aux_uids_buf, num_aux_uids);
+ TRASH_IDS (aux_gids, aux_gids_buf, num_aux_gids);
+
+ return err;
+}
+
+error_t
+S_io_restrict_auth (struct sock_user *user,
+ mach_port_t *new_port,
+ mach_msg_type_name_t *new_port_type,
+ uid_t *uids, unsigned num_uids,
+ uid_t *gids, unsigned num_gids)
+{
+ if (!user)
+ return EOPNOTSUPP;
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ return sock_create_port (user->sock, new_port);
+}
+
+error_t
+S_io_pathconf (struct sock_user *user, int name, int *value)
+{
+ if (user == NULL)
+ return EOPNOTSUPP;
+ else if (name == _PC_PIPE_BUF)
+ {
+ mutex_lock (&user->sock->lock);
+ if (user->sock->write_pipe == NULL)
+ *value = 0;
+ else
+ *value = user->sock->write_pipe->write_atomic;
+ mutex_unlock (&user->sock->lock);
+ return 0;
+ }
+ else
+ return EINVAL;
+}
+
+error_t
+S_io_identity (struct sock_user *user,
+ mach_port_t *id, mach_msg_type_name_t *id_type,
+ mach_port_t *fsys_id, mach_msg_type_name_t *fsys_id_type,
+ int *fileno)
+{
+ static mach_port_t server_id = MACH_PORT_NULL;
+ error_t err = 0;
+ struct sock *sock;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ if (server_id == MACH_PORT_NULL)
+ {
+ static struct mutex server_id_lock = MUTEX_INITIALIZER;
+
+ mutex_lock (&server_id_lock);
+ if (server_id == MACH_PORT_NULL) /* Recheck with the lock held. */
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &server_id);
+ mutex_unlock (&server_id_lock);
+
+ if (err)
+ return err;
+ }
+
+ sock = user->sock;
+
+ mutex_lock (&sock->lock);
+ if (sock->id == MACH_PORT_NULL)
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &sock->id);
+ mutex_unlock (&sock->lock);
+
+ if (! err)
+ {
+ *id = sock->id;
+ *id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fsys_id = server_id;
+ *fsys_id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = sock->id; /* Might as well */
+ }
+
+ return err;
+}
+
+
+/* Stubs for currently unsupported rpcs. */
+
+error_t
+S_io_revoke (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_async(struct sock_user *user,
+ mach_port_t notify_port,
+ mach_port_t *async_id_port,
+ mach_msg_type_name_t *async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_mod_owner(struct sock_user *user, pid_t owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_owner(struct sock_user *user, pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_icky_async_id (struct sock_user *user,
+ mach_port_t *icky_async_id_port,
+ mach_msg_type_name_t *icky_async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map (struct sock_user *user,
+ mach_port_t *memobj_rd, mach_msg_type_name_t *memobj_rd_type,
+ mach_port_t *memobj_wt, mach_msg_type_name_t *memobj_wt_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map_cntl (struct sock_user *user,
+ mach_port_t *mem, mach_msg_type_name_t *mem_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_release_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_eofnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_prenotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_postnotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readsleep (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+
+error_t
+S_io_sigio (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_server_version (struct sock_user *user,
+ char *name, int *maj, int *min, int *edit)
+{
+ return EOPNOTSUPP;
+}