summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/uipc_syscalls.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2016-10-07 15:10:20 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-01-10 09:53:31 +0100
commitc40e45b75eb76d79a05c7fa85c1fa9b5c728a12f (patch)
treead4f2519067709f00ab98b3c591186c26dc3a21f /freebsd/sys/kern/uipc_syscalls.c
parentuserspace-header-gen.py: Simplify program ports (diff)
downloadrtems-libbsd-c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f.tar.bz2
Update to FreeBSD head 2016-08-23
Git mirror commit 9fe7c416e6abb28b1398fd3e5687099846800cfd.
Diffstat (limited to 'freebsd/sys/kern/uipc_syscalls.c')
-rw-r--r--freebsd/sys/kern/uipc_syscalls.c1717
1 files changed, 307 insertions, 1410 deletions
diff --git a/freebsd/sys/kern/uipc_syscalls.c b/freebsd/sys/kern/uipc_syscalls.c
index c7a3dbe9..99ae6392 100644
--- a/freebsd/sys/kern/uipc_syscalls.c
+++ b/freebsd/sys/kern/uipc_syscalls.c
@@ -4,9 +4,6 @@
* Copyright (c) 1982, 1986, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
- * sendfile(2) and related extensions:
- * Copyright (c) 1998, David Greenman. All rights reserved.
- *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -40,37 +37,27 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_capsicum.h>
#include <rtems/bsd/local/opt_inet.h>
#include <rtems/bsd/local/opt_inet6.h>
-#include <rtems/bsd/local/opt_sctp.h>
#include <rtems/bsd/local/opt_compat.h>
#include <rtems/bsd/local/opt_ktrace.h>
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
-#include <sys/capability.h>
+#include <sys/capsicum.h>
#include <sys/kernel.h>
#include <rtems/bsd/sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysproto.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
-#include <sys/event.h>
#include <sys/proc.h>
-#include <sys/fcntl.h>
-#include <sys/file.h>
#include <sys/filio.h>
#include <sys/jail.h>
-#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
-#include <sys/sf_buf.h>
-#include <sys/sysent.h>
+#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
-#include <sys/signalvar.h>
#include <sys/syscallsubr.h>
-#include <sys/sysctl.h>
-#include <sys/uio.h>
-#include <sys/vnode.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
@@ -83,103 +70,44 @@ __FBSDID("$FreeBSD$");
#include <security/audit/audit.h>
#include <security/mac/mac_framework.h>
-#include <vm/vm.h>
-#include <vm/vm_param.h>
-#include <vm/vm_object.h>
-#include <vm/vm_page.h>
-#include <vm/vm_pageout.h>
-#include <vm/vm_kern.h>
-#include <vm/vm_extern.h>
-
-#if defined(INET) || defined(INET6)
-#ifdef SCTP
-#include <netinet/sctp.h>
-#include <netinet/sctp_peeloff.h>
-#endif /* SCTP */
-#endif /* INET || INET6 */
-#ifdef __rtems__
-#include <machine/rtems-bsd-syscall-api.h>
-#include <rtems/bsd/zerocopy.h>
-
-static int kern_bind(struct thread *, int, struct sockaddr *);
-
-static int kern_connect(struct thread *, int, struct sockaddr *);
-
-static int kern_setsockopt( struct thread *td, int s, int level, int name,
- void *val, enum uio_seg valseg, socklen_t valsize);
-
-static int kern_getsockopt( struct thread *td, int s, int level, int name,
- void *val, enum uio_seg valseg, socklen_t *valsize);
-#endif /* __rtems__ */
-
/*
- * Creation flags, OR'ed into socket() and socketpair() type argument.
- * For stable/9, these are supported but not exposed in the header file.
+ * Flags for accept1() and kern_accept4(), in addition to SOCK_CLOEXEC
+ * and SOCK_NONBLOCK.
*/
-#define SOCK_CLOEXEC 0x10000000
-#define SOCK_NONBLOCK 0x20000000
+#define ACCEPT4_INHERIT 0x1
+#define ACCEPT4_COMPAT 0x2
static int sendit(struct thread *td, int s, struct msghdr *mp, int flags);
static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp);
#ifndef __rtems__
-static int accept1(struct thread *td, struct accept_args *uap, int compat);
-static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat);
+static int accept1(struct thread *td, int s, struct sockaddr *uname,
+ socklen_t *anamelen, int flags);
static int getsockname1(struct thread *td, struct getsockname_args *uap,
int compat);
static int getpeername1(struct thread *td, struct getpeername_args *uap,
int compat);
+#endif /* __rtems__ */
+static int sockargs(struct mbuf **, char *, socklen_t, int);
+#ifndef __rtems__
/*
- * NSFBUFS-related variables and associated sysctls
- */
-int nsfbufs;
-int nsfbufspeak;
-int nsfbufsused;
-
-SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufs, CTLFLAG_RDTUN, &nsfbufs, 0,
- "Maximum number of sendfile(2) sf_bufs available");
-SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufspeak, CTLFLAG_RD, &nsfbufspeak, 0,
- "Number of sendfile(2) sf_bufs at peak usage");
-SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufsused, CTLFLAG_RD, &nsfbufsused, 0,
- "Number of sendfile(2) sf_bufs in use");
-
-/*
- * Convert a user file descriptor to a kernel file entry and check that, if
- * it is a capability, the right rights are present. A reference on the file
- * entry is held upon returning.
+ * Convert a user file descriptor to a kernel file entry and check if required
+ * capability rights are present.
+ * A reference on the file entry is held upon returning.
*/
-static int
-getsock_cap(struct filedesc *fdp, int fd, cap_rights_t rights,
+int
+getsock_cap(struct thread *td, int fd, cap_rights_t *rightsp,
struct file **fpp, u_int *fflagp)
{
struct file *fp;
-#ifdef CAPABILITIES
- struct file *fp_fromcap;
int error;
-#endif
- fp = NULL;
- if ((fdp == NULL) || ((fp = fget_unlocked(fdp, fd)) == NULL))
- return (EBADF);
-#ifdef CAPABILITIES
- /*
- * If the file descriptor is for a capability, test rights and use
- * the file descriptor referenced by the capability.
- */
- error = cap_funwrap(fp, rights, &fp_fromcap);
- if (error) {
- fdrop(fp, curthread);
+ error = fget_unlocked(td->td_proc->p_fd, fd, rightsp, &fp, NULL);
+ if (error != 0)
return (error);
- }
- if (fp != fp_fromcap) {
- fhold(fp_fromcap);
- fdrop(fp, curthread);
- fp = fp_fromcap;
- }
-#endif /* CAPABILITIES */
if (fp->f_type != DTYPE_SOCKET) {
- fdrop(fp, curthread);
+ fdrop(fp, td);
return (ENOTSOCK);
}
if (fflagp != NULL)
@@ -220,7 +148,7 @@ rtems_bsd_getsock(int fd, struct file **fpp, u_int *fflagp)
return (error);
}
-#define getsock_cap(fdp, fd, rights, fpp, fflagp) rtems_bsd_getsock(fd, fpp, fflagp)
+#define getsock_cap(td, fd, rights, fpp, fflagp) rtems_bsd_getsock(fd, fpp, fflagp)
#endif /* __rtems__ */
/*
@@ -242,9 +170,6 @@ sys_socket(td, uap)
int protocol;
} */ *uap;
{
-#ifndef __rtems__
- struct filedesc *fdp;
-#endif /* __rtems__ */
struct socket *so;
struct file *fp;
int fd, error, type, oflag, fflag;
@@ -268,20 +193,17 @@ sys_socket(td, uap)
#ifdef MAC
error = mac_socket_check_create(td->td_ucred, uap->domain, type,
uap->protocol);
- if (error)
+ if (error != 0)
return (error);
#endif
-#ifndef __rtems__
- fdp = td->td_proc->p_fd;
-#endif /* __rtems__ */
error = falloc(td, &fp, &fd, oflag);
- if (error)
+ if (error != 0)
return (error);
/* An extra reference on `fp' has been held for us by falloc(). */
error = socreate(uap->domain, &so, type, uap->protocol,
td->td_ucred, td);
- if (error) {
- fdclose(fdp, fp, fd, td);
+ if (error != 0) {
+ fdclose(td, fp, fd);
} else {
finit(fp, FREAD | FWRITE | fflag, DTYPE_SOCKET, so, &socketops);
if ((fflag & FNONBLOCK) != 0)
@@ -319,6 +241,9 @@ socket(int domain, int type, int protocol)
/* ARGSUSED */
#ifdef __rtems__
+static int kern_bindat(struct thread *td, int dirfd, int fd,
+ struct sockaddr *sa);
+
static
#endif /* __rtems__ */
int
@@ -333,11 +258,11 @@ sys_bind(td, uap)
struct sockaddr *sa;
int error;
- if ((error = getsockaddr(&sa, uap->name, uap->namelen)) != 0)
- return (error);
-
- error = kern_bind(td, uap->s, sa);
- free(sa, M_SONAME);
+ error = getsockaddr(&sa, uap->name, uap->namelen);
+ if (error == 0) {
+ error = kern_bindat(td, AT_FDCWD, uap->s, sa);
+ free(sa, M_SONAME);
+ }
return (error);
}
#ifdef __rtems__
@@ -363,18 +288,18 @@ bind(int socket, const struct sockaddr *address, socklen_t address_len)
#endif /* __rtems__ */
int
-kern_bind(td, fd, sa)
- struct thread *td;
- int fd;
- struct sockaddr *sa;
+kern_bindat(struct thread *td, int dirfd, int fd, struct sockaddr *sa)
{
struct socket *so;
struct file *fp;
+ cap_rights_t rights;
int error;
AUDIT_ARG_FD(fd);
- error = getsock_cap(td->td_proc->p_fd, fd, CAP_BIND, &fp, NULL);
- if (error)
+ AUDIT_ARG_SOCKADDR(td, dirfd, sa);
+ error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_BIND),
+ &fp, NULL);
+ if (error != 0)
return (error);
so = fp->f_data;
#ifdef KTRACE
@@ -383,17 +308,45 @@ kern_bind(td, fd, sa)
#endif
#ifdef MAC
error = mac_socket_check_bind(td->td_ucred, so, sa);
- if (error == 0)
+ if (error == 0) {
+#endif
+ if (dirfd == AT_FDCWD)
+ error = sobind(so, sa, td);
+ else
+ error = sobindat(dirfd, so, sa, td);
+#ifdef MAC
+ }
#endif
- error = sobind(so, sa, td);
fdrop(fp, td);
return (error);
}
/* ARGSUSED */
-#ifdef __rtems__
+#ifndef __rtems__
static
+int
+sys_bindat(td, uap)
+ struct thread *td;
+ struct bindat_args /* {
+ int fd;
+ int s;
+ caddr_t name;
+ int namelen;
+ } */ *uap;
+{
+ struct sockaddr *sa;
+ int error;
+
+ error = getsockaddr(&sa, uap->name, uap->namelen);
+ if (error == 0) {
+ error = kern_bindat(td, uap->fd, uap->s, sa);
+ free(sa, M_SONAME);
+ }
+ return (error);
+}
#endif /* __rtems__ */
+
+/* ARGSUSED */
int
sys_listen(td, uap)
struct thread *td;
@@ -404,10 +357,12 @@ sys_listen(td, uap)
{
struct socket *so;
struct file *fp;
+ cap_rights_t rights;
int error;
AUDIT_ARG_FD(uap->s);
- error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_LISTEN, &fp, NULL);
+ error = getsock_cap(td, uap->s, cap_rights_init(&rights, CAP_LISTEN),
+ &fp, NULL);
if (error == 0) {
so = fp->f_data;
#ifdef MAC
@@ -441,60 +396,50 @@ listen(int socket, int backlog)
#endif /* __rtems__ */
#ifdef __rtems__
-static int
-kern_accept(struct thread *td, int s, struct sockaddr **name,
- socklen_t *namelen, struct file **fp);
+static int kern_accept4(struct thread *td, int s, struct sockaddr **name,
+ socklen_t *namelen, int flags, struct file **fp);
#endif /* __rtems__ */
/*
* accept1()
*/
static int
-accept1(td, uap, compat)
+accept1(td, s, uname, anamelen, flags)
struct thread *td;
- struct accept_args /* {
- int s;
- struct sockaddr * __restrict name;
- socklen_t * __restrict anamelen;
- } */ *uap;
- int compat;
+ int s;
+ struct sockaddr *uname;
+ socklen_t *anamelen;
+ int flags;
{
struct sockaddr *name;
socklen_t namelen;
struct file *fp;
int error;
- if (uap->name == NULL)
- return (kern_accept(td, uap->s, NULL, NULL, NULL));
+ if (uname == NULL)
+ return (kern_accept4(td, s, NULL, NULL, flags, NULL));
- error = copyin(uap->anamelen, &namelen, sizeof (namelen));
- if (error)
+ error = copyin(anamelen, &namelen, sizeof (namelen));
+ if (error != 0)
return (error);
- error = kern_accept(td, uap->s, &name, &namelen, &fp);
+ error = kern_accept4(td, s, &name, &namelen, flags, &fp);
- /*
- * return a namelen of zero for older code which might
- * ignore the return value from accept.
- */
- if (error) {
- (void) copyout(&namelen,
- uap->anamelen, sizeof(*uap->anamelen));
+ if (error != 0)
return (error);
- }
- if (error == 0 && name != NULL) {
+ if (error == 0 && uname != NULL) {
#ifdef COMPAT_OLDSOCK
- if (compat)
+ if (flags & ACCEPT4_COMPAT)
((struct osockaddr *)name)->sa_family =
name->sa_family;
#endif
- error = copyout(name, uap->name, namelen);
+ error = copyout(name, uname, namelen);
}
if (error == 0)
- error = copyout(&namelen, uap->anamelen,
+ error = copyout(&namelen, anamelen,
sizeof(namelen));
- if (error)
- fdclose(td->td_proc->p_fd, fp, td->td_retval[0], td);
+ if (error != 0)
+ fdclose(td, fp, td->td_retval[0]);
fdrop(fp, td);
free(name, M_SONAME);
return (error);
@@ -505,15 +450,11 @@ accept(int socket, struct sockaddr *__restrict address,
socklen_t *__restrict address_len)
{
struct thread *td = rtems_bsd_get_curthread_or_null();
- struct accept_args ua = {
- .s = socket,
- .name = address,
- .anamelen = address_len
- };
int error;
if (td != NULL) {
- error = accept1(td, &ua);
+ error = accept1(td, socket, address, address_len,
+ ACCEPT4_INHERIT);
} else {
error = ENOMEM;
}
@@ -526,34 +467,34 @@ accept(int socket, struct sockaddr *__restrict address,
}
#endif /* __rtems__ */
+#ifndef __rtems__
int
kern_accept(struct thread *td, int s, struct sockaddr **name,
socklen_t *namelen, struct file **fp)
{
-#ifndef __rtems__
- struct filedesc *fdp;
+ return (kern_accept4(td, s, name, namelen, ACCEPT4_INHERIT, fp));
+}
#endif /* __rtems__ */
+
+int
+kern_accept4(struct thread *td, int s, struct sockaddr **name,
+ socklen_t *namelen, int flags, struct file **fp)
+{
struct file *headfp, *nfp = NULL;
struct sockaddr *sa = NULL;
- int error;
struct socket *head, *so;
- int fd;
+ cap_rights_t rights;
u_int fflag;
pid_t pgid;
- int tmp;
+ int error, fd, tmp;
- if (name) {
+ if (name != NULL)
*name = NULL;
- if (*namelen < 0)
- return (EINVAL);
- }
AUDIT_ARG_FD(s);
-#ifndef __rtems__
- fdp = td->td_proc->p_fd;
-#endif /* __rtems__ */
- error = getsock_cap(fdp, s, CAP_ACCEPT, &headfp, &fflag);
- if (error)
+ error = getsock_cap(td, s, cap_rights_init(&rights, CAP_ACCEPT),
+ &headfp, &fflag);
+ if (error != 0)
return (error);
head = headfp->f_data;
if ((head->so_options & SO_ACCEPTCONN) == 0) {
@@ -565,8 +506,8 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
if (error != 0)
goto done;
#endif
- error = falloc(td, &nfp, &fd, 0);
- if (error)
+ error = falloc(td, &nfp, &fd, (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0);
+ if (error != 0)
goto done;
ACCEPT_LOCK();
if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) {
@@ -581,7 +522,7 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
}
error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH,
"accept", 0);
- if (error) {
+ if (error != 0) {
ACCEPT_UNLOCK();
goto noconnection;
}
@@ -606,7 +547,10 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
TAILQ_REMOVE(&head->so_comp, so, so_list);
head->so_qlen--;
- so->so_state |= (head->so_state & SS_NBIO);
+ if (flags & ACCEPT4_INHERIT)
+ so->so_state |= (head->so_state & SS_NBIO);
+ else
+ so->so_state |= (flags & SOCK_NONBLOCK) ? SS_NBIO : 0;
so->so_qstate &= ~SQ_COMP;
so->so_head = NULL;
@@ -619,9 +563,15 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
/* connection has been removed from the listen queue */
KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0);
- pgid = fgetown(&head->so_sigio);
- if (pgid != 0)
- fsetown(pgid, &so->so_sigio);
+ if (flags & ACCEPT4_INHERIT) {
+ pgid = fgetown(&head->so_sigio);
+ if (pgid != 0)
+ fsetown(pgid, &so->so_sigio);
+ } else {
+ fflag &= ~(FNONBLOCK | FASYNC);
+ if (flags & SOCK_NONBLOCK)
+ fflag |= FNONBLOCK;
+ }
finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
/* Sync socket nonblocking/async state with file flags */
@@ -629,22 +579,16 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
(void) fo_ioctl(nfp, FIONBIO, &tmp, td->td_ucred, td);
tmp = fflag & FASYNC;
(void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td);
- sa = 0;
+ sa = NULL;
error = soaccept(so, &sa);
- if (error) {
- /*
- * return a namelen of zero for older code which might
- * ignore the return value from accept.
- */
- if (name)
- *namelen = 0;
+ if (error != 0)
goto noconnection;
- }
if (sa == NULL) {
if (name)
*namelen = 0;
goto done;
}
+ AUDIT_ARG_SOCKADDR(td, AT_FDCWD, sa);
if (name) {
/* check sa_len before it is destroyed */
if (*namelen > sa->sa_len)
@@ -657,15 +601,14 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
sa = NULL;
}
noconnection:
- if (sa)
- free(sa, M_SONAME);
+ free(sa, M_SONAME);
/*
* close the new descriptor, assuming someone hasn't ripped it
* out from under us.
*/
- if (error)
- fdclose(fdp, nfp, fd, td);
+ if (error != 0)
+ fdclose(td, nfp, fd);
/*
* Release explicitly held references before returning. We return
@@ -692,7 +635,19 @@ sys_accept(td, uap)
struct accept_args *uap;
{
- return (accept1(td, uap, 0));
+ return (accept1(td, uap->s, uap->name, uap->anamelen, ACCEPT4_INHERIT));
+}
+
+int
+sys_accept4(td, uap)
+ struct thread *td;
+ struct accept4_args *uap;
+{
+
+ if (uap->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
+ return (EINVAL);
+
+ return (accept1(td, uap->s, uap->name, uap->anamelen, uap->flags));
}
#ifdef COMPAT_OLDSOCK
@@ -702,13 +657,17 @@ oaccept(td, uap)
struct accept_args *uap;
{
- return (accept1(td, uap, 1));
+ return (accept1(td, uap->s, uap->name, uap->anamelen,
+ ACCEPT4_INHERIT | ACCEPT4_COMPAT));
}
#endif /* COMPAT_OLDSOCK */
#endif /* __rtems__ */
/* ARGSUSED */
#ifdef __rtems__
+static int kern_connectat(struct thread *td, int dirfd, int fd,
+ struct sockaddr *sa);
+
static
#endif /* __rtems__ */
int
@@ -724,11 +683,10 @@ sys_connect(td, uap)
int error;
error = getsockaddr(&sa, uap->name, uap->namelen);
- if (error)
- return (error);
-
- error = kern_connect(td, uap->s, sa);
- free(sa, M_SONAME);
+ if (error == 0) {
+ error = kern_connectat(td, AT_FDCWD, uap->s, sa);
+ free(sa, M_SONAME);
+ }
return (error);
}
#ifdef __rtems__
@@ -753,21 +711,19 @@ connect(int socket, const struct sockaddr *address, socklen_t address_len)
}
#endif /* __rtems__ */
-
int
-kern_connect(td, fd, sa)
- struct thread *td;
- int fd;
- struct sockaddr *sa;
+kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa)
{
struct socket *so;
struct file *fp;
- int error;
- int interrupted = 0;
+ cap_rights_t rights;
+ int error, interrupted = 0;
AUDIT_ARG_FD(fd);
- error = getsock_cap(td->td_proc->p_fd, fd, CAP_CONNECT, &fp, NULL);
- if (error)
+ AUDIT_ARG_SOCKADDR(td, dirfd, sa);
+ error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_CONNECT),
+ &fp, NULL);
+ if (error != 0)
return (error);
so = fp->f_data;
if (so->so_state & SS_ISCONNECTING) {
@@ -780,11 +736,14 @@ kern_connect(td, fd, sa)
#endif
#ifdef MAC
error = mac_socket_check_connect(td->td_ucred, so, sa);
- if (error)
+ if (error != 0)
goto bad;
#endif
- error = soconnect(so, sa, td);
- if (error)
+ if (dirfd == AT_FDCWD)
+ error = soconnect(so, sa, td);
+ else
+ error = soconnectat(dirfd, so, sa, td);
+ if (error != 0)
goto bad;
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
error = EINPROGRESS;
@@ -794,7 +753,7 @@ kern_connect(td, fd, sa)
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH,
"connec", 0);
- if (error) {
+ if (error != 0) {
if (error == EINTR || error == ERESTART)
interrupted = 1;
break;
@@ -815,13 +774,34 @@ done1:
return (error);
}
+#ifndef __rtems__
+/* ARGSUSED */
+int
+sys_connectat(td, uap)
+ struct thread *td;
+ struct connectat_args /* {
+ int fd;
+ int s;
+ caddr_t name;
+ int namelen;
+ } */ *uap;
+{
+ struct sockaddr *sa;
+ int error;
+
+ error = getsockaddr(&sa, uap->name, uap->namelen);
+ if (error == 0) {
+ error = kern_connectat(td, uap->fd, uap->s, sa);
+ free(sa, M_SONAME);
+ }
+ return (error);
+}
+#endif /* __rtems__ */
+
int
kern_socketpair(struct thread *td, int domain, int type, int protocol,
int *rsv)
{
-#ifndef __rtems__
- struct filedesc *fdp = td->td_proc->p_fd;
-#endif /* __rtems__ */
struct file *fp1, *fp2;
struct socket *so1, *so2;
int fd, error, oflag, fflag;
@@ -844,35 +824,35 @@ kern_socketpair(struct thread *td, int domain, int type, int protocol,
/* We might want to have a separate check for socket pairs. */
error = mac_socket_check_create(td->td_ucred, domain, type,
protocol);
- if (error)
+ if (error != 0)
return (error);
#endif
error = socreate(domain, &so1, type, protocol, td->td_ucred, td);
- if (error)
+ if (error != 0)
return (error);
error = socreate(domain, &so2, type, protocol, td->td_ucred, td);
- if (error)
+ if (error != 0)
goto free1;
/* On success extra reference to `fp1' and 'fp2' is set by falloc. */
error = falloc(td, &fp1, &fd, oflag);
- if (error)
+ if (error != 0)
goto free2;
rsv[0] = fd;
fp1->f_data = so1; /* so1 already has ref count */
error = falloc(td, &fp2, &fd, oflag);
- if (error)
+ if (error != 0)
goto free3;
fp2->f_data = so2; /* so2 already has ref count */
rsv[1] = fd;
error = soconnect2(so1, so2);
- if (error)
+ if (error != 0)
goto free4;
if (type == SOCK_DGRAM) {
/*
* Datagram socket connection is asymmetric.
*/
error = soconnect2(so2, so1);
- if (error)
+ if (error != 0)
goto free4;
}
finit(fp1, FREAD | FWRITE | fflag, DTYPE_SOCKET, fp1->f_data,
@@ -887,10 +867,10 @@ kern_socketpair(struct thread *td, int domain, int type, int protocol,
fdrop(fp2, td);
return (0);
free4:
- fdclose(fdp, fp2, rsv[1], td);
+ fdclose(td, fp2, rsv[1]);
fdrop(fp2, td);
free3:
- fdclose(fdp, fp1, rsv[0], td);
+ fdclose(td, fp1, rsv[0]);
fdrop(fp1, td);
free2:
if (so2 != NULL)
@@ -916,11 +896,11 @@ sys_socketpair(struct thread *td, struct socketpair_args *uap)
error = kern_socketpair(td, uap->domain, uap->type,
uap->protocol, sv);
- if (error)
+ if (error != 0)
return (error);
#ifndef __rtems__
error = copyout(sv, uap->rsv, 2 * sizeof(int));
- if (error) {
+ if (error != 0) {
(void)kern_close(td, sv[0]);
(void)kern_close(td, sv[1]);
}
@@ -973,7 +953,7 @@ sendit(td, s, mp, flags)
if (mp->msg_name != NULL) {
error = getsockaddr(&to, mp->msg_name, mp->msg_namelen);
- if (error) {
+ if (error != 0) {
to = NULL;
goto bad;
}
@@ -993,13 +973,13 @@ sendit(td, s, mp, flags)
}
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, MT_CONTROL);
- if (error)
+ if (error != 0)
goto bad;
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags == MSG_COMPAT) {
struct cmsghdr *cm;
- M_PREPEND(control, sizeof(*cm), M_WAIT);
+ M_PREPEND(control, sizeof(*cm), M_WAITOK);
cm = mtod(control, struct cmsghdr *);
cm->cmsg_len = control->m_len;
cm->cmsg_level = SOL_SOCKET;
@@ -1013,8 +993,7 @@ sendit(td, s, mp, flags)
error = kern_sendit(td, s, mp, flags, control, UIO_USERSPACE);
bad:
- if (to)
- free(to, M_SONAME);
+ free(to, M_SONAME);
return (error);
}
@@ -1031,20 +1010,24 @@ kern_sendit(td, s, mp, flags, control, segflg)
struct uio auio;
struct iovec *iov;
struct socket *so;
- int i, error;
- ssize_t len;
#ifndef __rtems__
cap_rights_t rights;
#endif /* __rtems__ */
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
+ ssize_t len;
+ int i, error;
AUDIT_ARG_FD(s);
#ifndef __rtems__
- rights = CAP_WRITE;
- if (mp->msg_name != NULL)
- rights |= CAP_CONNECT;
+ cap_rights_init(&rights, CAP_SEND);
+ if (mp->msg_name != NULL) {
+ AUDIT_ARG_SOCKADDR(td, AT_FDCWD, mp->msg_name);
+ cap_rights_set(&rights, CAP_CONNECT);
+ }
+ error = getsock_cap(td, s, &rights, &fp, NULL);
+ if (error != 0)
#endif /* __rtems__ */
error = getsock_cap(td->td_proc->p_fd, s, rights, &fp, NULL);
if (error)
@@ -1059,11 +1042,11 @@ kern_sendit(td, s, mp, flags, control, segflg)
if (mp->msg_name != NULL) {
error = mac_socket_check_connect(td->td_ucred, so,
mp->msg_name);
- if (error)
+ if (error != 0)
goto bad;
}
error = mac_socket_check_send(td->td_ucred, so);
- if (error)
+ if (error != 0)
goto bad;
#endif
@@ -1087,7 +1070,7 @@ kern_sendit(td, s, mp, flags, control, segflg)
#endif
len = auio.uio_resid;
error = sosend(so, mp->msg_name, &auio, 0, control, flags, td);
- if (error) {
+ if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
@@ -1133,7 +1116,6 @@ sys_sendto(td, uap)
{
struct msghdr msg;
struct iovec aiov;
- int error;
msg.msg_name = uap->to;
msg.msg_namelen = uap->tolen;
@@ -1145,8 +1127,7 @@ sys_sendto(td, uap)
#endif
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
- error = sendit(td, uap->s, &msg, uap->flags);
- return (error);
+ return (sendit(td, uap->s, &msg, uap->flags));
}
#ifdef __rtems__
ssize_t
@@ -1216,7 +1197,6 @@ osend(td, uap)
{
struct msghdr msg;
struct iovec aiov;
- int error;
msg.msg_name = 0;
msg.msg_namelen = 0;
@@ -1226,8 +1206,7 @@ osend(td, uap)
aiov.iov_len = uap->len;
msg.msg_control = 0;
msg.msg_flags = 0;
- error = sendit(td, uap->s, &msg, uap->flags);
- return (error);
+ return (sendit(td, uap->s, &msg, uap->flags));
}
int
@@ -1244,10 +1223,10 @@ osendmsg(td, uap)
int error;
error = copyin(uap->msg, &msg, sizeof (struct omsghdr));
- if (error)
+ if (error != 0)
return (error);
error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
- if (error)
+ if (error != 0)
return (error);
msg.msg_iov = iov;
msg.msg_flags = MSG_COMPAT;
@@ -1275,10 +1254,10 @@ sys_sendmsg(td, uap)
int error;
error = copyin(uap->msg, &msg, sizeof (msg));
- if (error)
+ if (error != 0)
return (error);
error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
- if (error)
+ if (error != 0)
return (error);
msg.msg_iov = iov;
#ifdef COMPAT_OLDSOCK
@@ -1327,30 +1306,31 @@ kern_recvit(td, s, mp, fromseg, controlp)
{
struct uio auio;
struct iovec *iov;
- int i;
- ssize_t len;
- int error;
- struct mbuf *m, *control = 0;
+ struct mbuf *m, *control = NULL;
caddr_t ctlbuf;
struct file *fp;
struct socket *so;
- struct sockaddr *fromsa = 0;
+ struct sockaddr *fromsa = NULL;
+ cap_rights_t rights;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
+ ssize_t len;
+ int error, i;
if (controlp != NULL)
*controlp = NULL;
AUDIT_ARG_FD(s);
- error = getsock_cap(td->td_proc->p_fd, s, CAP_READ, &fp, NULL);
- if (error)
+ error = getsock_cap(td, s, cap_rights_init(&rights, CAP_RECV),
+ &fp, NULL);
+ if (error != 0)
return (error);
so = fp->f_data;
#ifdef MAC
error = mac_socket_check_receive(td->td_ucred, so);
- if (error) {
+ if (error != 0) {
fdrop(fp, td);
return (error);
}
@@ -1375,26 +1355,28 @@ kern_recvit(td, s, mp, fromseg, controlp)
ktruio = cloneuio(&auio);
#endif
len = auio.uio_resid;
- error = soreceive(so, &fromsa, &auio, (struct mbuf **)0,
- (mp->msg_control || controlp) ? &control : (struct mbuf **)0,
+ error = soreceive(so, &fromsa, &auio, NULL,
+ (mp->msg_control || controlp) ? &control : NULL,
&mp->msg_flags);
- if (error) {
+ if (error != 0) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
}
+ if (fromsa != NULL)
+ AUDIT_ARG_SOCKADDR(td, AT_FDCWD, fromsa);
#ifdef KTRACE
if (ktruio != NULL) {
ktruio->uio_resid = len - auio.uio_resid;
ktrgenio(s, UIO_READ, ktruio, error);
}
#endif
- if (error)
+ if (error != 0)
goto out;
td->td_retval[0] = len - auio.uio_resid;
if (mp->msg_name) {
len = mp->msg_namelen;
- if (len <= 0 || fromsa == 0)
+ if (len <= 0 || fromsa == NULL)
len = 0;
else {
/* save sa_len before it is destroyed by MSG_COMPAT */
@@ -1407,7 +1389,7 @@ kern_recvit(td, s, mp, fromseg, controlp)
if (fromseg == UIO_USERSPACE) {
error = copyout(fromsa, mp->msg_name,
(unsigned)len);
- if (error)
+ if (error != 0)
goto out;
} else
bcopy(fromsa, mp->msg_name, len);
@@ -1466,10 +1448,9 @@ out:
if (fromsa && KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(fromsa);
#endif
- if (fromsa)
- free(fromsa, M_SONAME);
+ free(fromsa, M_SONAME);
- if (error == 0 && controlp != NULL)
+ if (error == 0 && controlp != NULL)
*controlp = control;
else if (control)
m_freem(control);
@@ -1487,9 +1468,9 @@ recvit(td, s, mp, namelenp)
int error;
error = kern_recvit(td, s, mp, UIO_USERSPACE, NULL);
- if (error)
+ if (error != 0)
return (error);
- if (namelenp) {
+ if (namelenp != NULL) {
error = copyout(&mp->msg_namelen, namelenp, sizeof (socklen_t));
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
@@ -1521,7 +1502,7 @@ sys_recvfrom(td, uap)
if (uap->fromlenaddr) {
error = copyin(uap->fromlenaddr,
&msg.msg_namelen, sizeof (msg.msg_namelen));
- if (error)
+ if (error != 0)
goto done2;
} else {
msg.msg_namelen = 0;
@@ -1535,7 +1516,7 @@ sys_recvfrom(td, uap)
msg.msg_flags = uap->flags;
error = recvit(td, uap->s, &msg, uap->fromlenaddr);
done2:
- return(error);
+ return (error);
}
#ifdef __rtems__
ssize_t
@@ -1593,7 +1574,6 @@ orecv(td, uap)
{
struct msghdr msg;
struct iovec aiov;
- int error;
msg.msg_name = 0;
msg.msg_namelen = 0;
@@ -1603,8 +1583,7 @@ orecv(td, uap)
aiov.iov_len = uap->len;
msg.msg_control = 0;
msg.msg_flags = uap->flags;
- error = recvit(td, uap->s, &msg, NULL);
- return (error);
+ return (recvit(td, uap->s, &msg, NULL));
}
/*
@@ -1626,10 +1605,10 @@ orecvmsg(td, uap)
int error;
error = copyin(uap->msg, &msg, sizeof (struct omsghdr));
- if (error)
+ if (error != 0)
return (error);
error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
- if (error)
+ if (error != 0)
return (error);
msg.msg_flags = uap->flags | MSG_COMPAT;
msg.msg_iov = iov;
@@ -1660,10 +1639,10 @@ sys_recvmsg(td, uap)
int error;
error = copyin(uap->msg, &msg, sizeof (msg));
- if (error)
+ if (error != 0)
return (error);
error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
- if (error)
+ if (error != 0)
return (error);
msg.msg_flags = uap->flags;
#ifdef COMPAT_OLDSOCK
@@ -1719,14 +1698,26 @@ sys_shutdown(td, uap)
{
struct socket *so;
struct file *fp;
+ cap_rights_t rights;
int error;
AUDIT_ARG_FD(uap->s);
- error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_SHUTDOWN, &fp,
- NULL);
+ error = getsock_cap(td, uap->s, cap_rights_init(&rights, CAP_SHUTDOWN),
+ &fp, NULL);
if (error == 0) {
so = fp->f_data;
error = soshutdown(so, uap->how);
+#ifndef __rtems__
+ /*
+ * Previous versions did not return ENOTCONN, but 0 in
+ * case the socket was not connected. Some important
+ * programs like syslogd up to r279016, 2015-02-19,
+ * still depend on this behavior.
+ */
+ if (error == ENOTCONN &&
+ td->td_proc->p_osrel < P_OSREL_SHUTDOWN_ENOTCONN)
+ error = 0;
+#endif /* __rtems__ */
fdrop(fp, td);
}
return (error);
@@ -1747,6 +1738,9 @@ shutdown(int socket, int how)
/* ARGSUSED */
#ifdef __rtems__
+static int kern_setsockopt( struct thread *td, int s, int level, int name,
+ void *val, enum uio_seg valseg, socklen_t valsize);
+
static
#endif /* __rtems__ */
int
@@ -1799,10 +1793,11 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize)
enum uio_seg valseg;
socklen_t valsize;
{
- int error;
struct socket *so;
struct file *fp;
struct sockopt sopt;
+ cap_rights_t rights;
+ int error;
if (val == NULL && valsize != 0)
return (EFAULT);
@@ -1826,7 +1821,8 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize)
}
AUDIT_ARG_FD(s);
- error = getsock_cap(td->td_proc->p_fd, s, CAP_SETSOCKOPT, &fp, NULL);
+ error = getsock_cap(td, s, cap_rights_init(&rights, CAP_SETSOCKOPT),
+ &fp, NULL);
if (error == 0) {
so = fp->f_data;
error = sosetopt(so, &sopt);
@@ -1837,6 +1833,9 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize)
/* ARGSUSED */
#ifdef __rtems__
+static int kern_getsockopt( struct thread *td, int s, int level, int name,
+ void *val, enum uio_seg valseg, socklen_t *valsize);
+
static
#endif /* __rtems__ */
int
@@ -1851,11 +1850,11 @@ sys_getsockopt(td, uap)
} */ *uap;
{
socklen_t valsize;
- int error;
+ int error;
if (uap->val) {
error = copyin(uap->avalsize, &valsize, sizeof (valsize));
- if (error)
+ if (error != 0)
return (error);
}
@@ -1905,10 +1904,11 @@ kern_getsockopt(td, s, level, name, val, valseg, valsize)
enum uio_seg valseg;
socklen_t *valsize;
{
- int error;
- struct socket *so;
+ struct socket *so;
struct file *fp;
- struct sockopt sopt;
+ struct sockopt sopt;
+ cap_rights_t rights;
+ int error;
if (val == NULL)
*valsize = 0;
@@ -1932,7 +1932,8 @@ kern_getsockopt(td, s, level, name, val, valseg, valsize)
}
AUDIT_ARG_FD(s);
- error = getsock_cap(td->td_proc->p_fd, s, CAP_GETSOCKOPT, &fp, NULL);
+ error = getsock_cap(td, s, cap_rights_init(&rights, CAP_GETSOCKOPT),
+ &fp, NULL);
if (error == 0) {
so = fp->f_data;
error = sogetopt(so, &sopt);
@@ -1966,11 +1967,11 @@ getsockname1(td, uap, compat)
int error;
error = copyin(uap->alen, &len, sizeof(len));
- if (error)
+ if (error != 0)
return (error);
error = kern_getsockname(td, uap->fdes, &sa, &len);
- if (error)
+ if (error != 0)
return (error);
if (len != 0) {
@@ -2014,22 +2015,21 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa,
{
struct socket *so;
struct file *fp;
+ cap_rights_t rights;
socklen_t len;
int error;
- if (*alen < 0)
- return (EINVAL);
-
AUDIT_ARG_FD(fd);
- error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETSOCKNAME, &fp, NULL);
- if (error)
+ error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_GETSOCKNAME),
+ &fp, NULL);
+ if (error != 0)
return (error);
so = fp->f_data;
*sa = NULL;
CURVNET_SET(so->so_vnet);
error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, sa);
CURVNET_RESTORE();
- if (error)
+ if (error != 0)
goto bad;
if (*sa == NULL)
len = 0;
@@ -2042,7 +2042,7 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa,
#endif
bad:
fdrop(fp, td);
- if (error && *sa) {
+ if (error != 0 && *sa != NULL) {
free(*sa, M_SONAME);
*sa = NULL;
}
@@ -2095,11 +2095,11 @@ getpeername1(td, uap, compat)
int error;
error = copyin(uap->alen, &len, sizeof (len));
- if (error)
+ if (error != 0)
return (error);
error = kern_getpeername(td, uap->fdes, &sa, &len);
- if (error)
+ if (error != 0)
return (error);
if (len != 0) {
@@ -2143,15 +2143,14 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa,
{
struct socket *so;
struct file *fp;
+ cap_rights_t rights;
socklen_t len;
int error;
- if (*alen < 0)
- return (EINVAL);
-
AUDIT_ARG_FD(fd);
- error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETPEERNAME, &fp, NULL);
- if (error)
+ error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_GETPEERNAME),
+ &fp, NULL);
+ if (error != 0)
return (error);
so = fp->f_data;
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) {
@@ -2162,7 +2161,7 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa,
CURVNET_SET(so->so_vnet);
error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, sa);
CURVNET_RESTORE();
- if (error)
+ if (error != 0)
goto bad;
if (*sa == NULL)
len = 0;
@@ -2174,7 +2173,7 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa,
ktrsockaddr(*sa);
#endif
bad:
- if (error && *sa) {
+ if (error != 0 && *sa != NULL) {
free(*sa, M_SONAME);
*sa = NULL;
}
@@ -2206,31 +2205,26 @@ ogetpeername(td, uap)
#endif /* COMPAT_OLDSOCK */
#endif /* __rtems__ */
-int
-sockargs(mp, buf, buflen, type)
- struct mbuf **mp;
- caddr_t buf;
- int buflen, type;
+static int
+sockargs(struct mbuf **mp, char *buf, socklen_t buflen, int type)
{
struct sockaddr *sa;
struct mbuf *m;
int error;
- if ((u_int)buflen > MLEN) {
+ if (buflen > MLEN) {
#ifdef COMPAT_OLDSOCK
- if (type == MT_SONAME && (u_int)buflen <= 112)
+ if (type == MT_SONAME && buflen <= 112)
buflen = MLEN; /* unix domain compat. hack */
else
#endif
- if ((u_int)buflen > MCLBYTES)
+ if (buflen > MCLBYTES)
return (EINVAL);
}
- m = m_get(M_WAIT, type);
- if ((u_int)buflen > MLEN)
- MCLGET(m, M_WAIT);
+ m = m_get2(buflen, M_WAITOK, type, 0);
m->m_len = buflen;
- error = copyin(buf, mtod(m, caddr_t), (u_int)buflen);
- if (error)
+ error = copyin(buf, mtod(m, void *), buflen);
+ if (error != 0)
(void) m_free(m);
else {
*mp = m;
@@ -2262,7 +2256,7 @@ getsockaddr(namp, uaddr, len)
return (EINVAL);
sa = malloc(len, M_SONAME, M_WAITOK);
error = copyin(uaddr, sa, len);
- if (error) {
+ if (error != 0) {
free(sa, M_SONAME);
} else {
#if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
@@ -2274,1100 +2268,3 @@ getsockaddr(namp, uaddr, len)
}
return (error);
}
-
-#ifndef __rtems__
-#include <sys/condvar.h>
-
-struct sendfile_sync {
- struct mtx mtx;
- struct cv cv;
- unsigned count;
-};
-
-/*
- * Detach mapped page and release resources back to the system.
- */
-void
-sf_buf_mext(void *addr, void *args)
-{
- vm_page_t m;
- struct sendfile_sync *sfs;
-
- m = sf_buf_page(args);
- sf_buf_free(args);
- vm_page_lock(m);
- vm_page_unwire(m, 0);
- /*
- * Check for the object going away on us. This can
- * happen since we don't hold a reference to it.
- * If so, we're responsible for freeing the page.
- */
- if (m->wire_count == 0 && m->object == NULL)
- vm_page_free(m);
- vm_page_unlock(m);
- if (addr == NULL)
- return;
- sfs = addr;
- mtx_lock(&sfs->mtx);
- KASSERT(sfs->count> 0, ("Sendfile sync botchup count == 0"));
- if (--sfs->count == 0)
- cv_signal(&sfs->cv);
- mtx_unlock(&sfs->mtx);
-}
-
-/*
- * sendfile(2)
- *
- * int sendfile(int fd, int s, off_t offset, size_t nbytes,
- * struct sf_hdtr *hdtr, off_t *sbytes, int flags)
- *
- * Send a file specified by 'fd' and starting at 'offset' to a socket
- * specified by 's'. Send only 'nbytes' of the file or until EOF if nbytes ==
- * 0. Optionally add a header and/or trailer to the socket output. If
- * specified, write the total number of bytes sent into *sbytes.
- */
-int
-sys_sendfile(struct thread *td, struct sendfile_args *uap)
-{
-
- return (do_sendfile(td, uap, 0));
-}
-
-static int
-do_sendfile(struct thread *td, struct sendfile_args *uap, int compat)
-{
- struct sf_hdtr hdtr;
- struct uio *hdr_uio, *trl_uio;
- int error;
-
- hdr_uio = trl_uio = NULL;
-
- if (uap->hdtr != NULL) {
- error = copyin(uap->hdtr, &hdtr, sizeof(hdtr));
- if (error)
- goto out;
- if (hdtr.headers != NULL) {
- error = copyinuio(hdtr.headers, hdtr.hdr_cnt, &hdr_uio);
- if (error)
- goto out;
- }
- if (hdtr.trailers != NULL) {
- error = copyinuio(hdtr.trailers, hdtr.trl_cnt, &trl_uio);
- if (error)
- goto out;
-
- }
- }
-
- error = kern_sendfile(td, uap, hdr_uio, trl_uio, compat);
-out:
- if (hdr_uio)
- free(hdr_uio, M_IOV);
- if (trl_uio)
- free(trl_uio, M_IOV);
- return (error);
-}
-
-#ifdef COMPAT_FREEBSD4
-int
-freebsd4_sendfile(struct thread *td, struct freebsd4_sendfile_args *uap)
-{
- struct sendfile_args args;
-
- args.fd = uap->fd;
- args.s = uap->s;
- args.offset = uap->offset;
- args.nbytes = uap->nbytes;
- args.hdtr = uap->hdtr;
- args.sbytes = uap->sbytes;
- args.flags = uap->flags;
-
- return (do_sendfile(td, &args, 1));
-}
-#endif /* COMPAT_FREEBSD4 */
-
-int
-kern_sendfile(struct thread *td, struct sendfile_args *uap,
- struct uio *hdr_uio, struct uio *trl_uio, int compat)
-{
- struct file *sock_fp;
- struct vnode *vp;
- struct vm_object *obj = NULL;
- struct socket *so = NULL;
- struct mbuf *m = NULL;
- struct sf_buf *sf;
- struct vm_page *pg;
- struct vattr va;
- off_t off, xfsize, fsbytes = 0, sbytes = 0, rem = 0;
- int error, hdrlen = 0, mnw = 0;
- int vfslocked;
- int bsize;
- struct sendfile_sync *sfs = NULL;
-
- /*
- * The file descriptor must be a regular file and have a
- * backing VM object.
- * File offset must be positive. If it goes beyond EOF
- * we send only the header/trailer and no payload data.
- */
- AUDIT_ARG_FD(uap->fd);
- if ((error = fgetvp_read(td, uap->fd, CAP_READ, &vp)) != 0)
- goto out;
- vfslocked = VFS_LOCK_GIANT(vp->v_mount);
- vn_lock(vp, LK_SHARED | LK_RETRY);
- if (vp->v_type == VREG) {
- bsize = vp->v_mount->mnt_stat.f_iosize;
- if (uap->nbytes == 0) {
- error = VOP_GETATTR(vp, &va, td->td_ucred);
- if (error != 0) {
- VOP_UNLOCK(vp, 0);
- VFS_UNLOCK_GIANT(vfslocked);
- obj = NULL;
- goto out;
- }
- rem = va.va_size;
- } else
- rem = uap->nbytes;
- obj = vp->v_object;
- if (obj != NULL) {
- /*
- * Temporarily increase the backing VM
- * object's reference count so that a forced
- * reclamation of its vnode does not
- * immediately destroy it.
- */
- VM_OBJECT_LOCK(obj);
- if ((obj->flags & OBJ_DEAD) == 0) {
- vm_object_reference_locked(obj);
- VM_OBJECT_UNLOCK(obj);
- } else {
- VM_OBJECT_UNLOCK(obj);
- obj = NULL;
- }
- }
- } else
- bsize = 0; /* silence gcc */
- VOP_UNLOCK(vp, 0);
- VFS_UNLOCK_GIANT(vfslocked);
- if (obj == NULL) {
- error = EINVAL;
- goto out;
- }
- if (uap->offset < 0) {
- error = EINVAL;
- goto out;
- }
-
- /*
- * The socket must be a stream socket and connected.
- * Remember if it a blocking or non-blocking socket.
- */
- if ((error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_WRITE,
- &sock_fp, NULL)) != 0)
- goto out;
- so = sock_fp->f_data;
- if (so->so_type != SOCK_STREAM) {
- error = EINVAL;
- goto out;
- }
- if ((so->so_state & SS_ISCONNECTED) == 0) {
- error = ENOTCONN;
- goto out;
- }
- /*
- * Do not wait on memory allocations but return ENOMEM for
- * caller to retry later.
- * XXX: Experimental.
- */
- if (uap->flags & SF_MNOWAIT)
- mnw = 1;
-
- if (uap->flags & SF_SYNC) {
- sfs = malloc(sizeof *sfs, M_TEMP, M_WAITOK | M_ZERO);
- mtx_init(&sfs->mtx, "sendfile", NULL, MTX_DEF);
- cv_init(&sfs->cv, "sendfile");
- }
-
-#ifdef MAC
- error = mac_socket_check_send(td->td_ucred, so);
- if (error)
- goto out;
-#endif
-
- /* If headers are specified copy them into mbufs. */
- if (hdr_uio != NULL) {
- hdr_uio->uio_td = td;
- hdr_uio->uio_rw = UIO_WRITE;
- if (hdr_uio->uio_resid > 0) {
- /*
- * In FBSD < 5.0 the nbytes to send also included
- * the header. If compat is specified subtract the
- * header size from nbytes.
- */
- if (compat) {
- if (uap->nbytes > hdr_uio->uio_resid)
- uap->nbytes -= hdr_uio->uio_resid;
- else
- uap->nbytes = 0;
- }
- m = m_uiotombuf(hdr_uio, (mnw ? M_NOWAIT : M_WAITOK),
- 0, 0, 0);
- if (m == NULL) {
- error = mnw ? EAGAIN : ENOBUFS;
- goto out;
- }
- hdrlen = m_length(m, NULL);
- }
- }
-
- /*
- * Protect against multiple writers to the socket.
- *
- * XXXRW: Historically this has assumed non-interruptibility, so now
- * we implement that, but possibly shouldn't.
- */
- (void)sblock(&so->so_snd, SBL_WAIT | SBL_NOINTR);
-
- /*
- * Loop through the pages of the file, starting with the requested
- * offset. Get a file page (do I/O if necessary), map the file page
- * into an sf_buf, attach an mbuf header to the sf_buf, and queue
- * it on the socket.
- * This is done in two loops. The inner loop turns as many pages
- * as it can, up to available socket buffer space, without blocking
- * into mbufs to have it bulk delivered into the socket send buffer.
- * The outer loop checks the state and available space of the socket
- * and takes care of the overall progress.
- */
- for (off = uap->offset; ; ) {
- struct mbuf *mtail;
- int loopbytes;
- int space;
- int done;
-
- if ((uap->nbytes != 0 && uap->nbytes == fsbytes) ||
- (uap->nbytes == 0 && va.va_size == fsbytes))
- break;
-
- mtail = NULL;
- loopbytes = 0;
- space = 0;
- done = 0;
-
- /*
- * Check the socket state for ongoing connection,
- * no errors and space in socket buffer.
- * If space is low allow for the remainder of the
- * file to be processed if it fits the socket buffer.
- * Otherwise block in waiting for sufficient space
- * to proceed, or if the socket is nonblocking, return
- * to userland with EAGAIN while reporting how far
- * we've come.
- * We wait until the socket buffer has significant free
- * space to do bulk sends. This makes good use of file
- * system read ahead and allows packet segmentation
- * offloading hardware to take over lots of work. If
- * we were not careful here we would send off only one
- * sfbuf at a time.
- */
- SOCKBUF_LOCK(&so->so_snd);
- if (so->so_snd.sb_lowat < so->so_snd.sb_hiwat / 2)
- so->so_snd.sb_lowat = so->so_snd.sb_hiwat / 2;
-retry_space:
- if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
- error = EPIPE;
- SOCKBUF_UNLOCK(&so->so_snd);
- goto done;
- } else if (so->so_error) {
- error = so->so_error;
- so->so_error = 0;
- SOCKBUF_UNLOCK(&so->so_snd);
- goto done;
- }
- space = sbspace(&so->so_snd);
- if (space < rem &&
- (space <= 0 ||
- space < so->so_snd.sb_lowat)) {
- if (so->so_state & SS_NBIO) {
- SOCKBUF_UNLOCK(&so->so_snd);
- error = EAGAIN;
- goto done;
- }
- /*
- * sbwait drops the lock while sleeping.
- * When we loop back to retry_space the
- * state may have changed and we retest
- * for it.
- */
- error = sbwait(&so->so_snd);
- /*
- * An error from sbwait usually indicates that we've
- * been interrupted by a signal. If we've sent anything
- * then return bytes sent, otherwise return the error.
- */
- if (error) {
- SOCKBUF_UNLOCK(&so->so_snd);
- goto done;
- }
- goto retry_space;
- }
- SOCKBUF_UNLOCK(&so->so_snd);
-
- /*
- * Reduce space in the socket buffer by the size of
- * the header mbuf chain.
- * hdrlen is set to 0 after the first loop.
- */
- space -= hdrlen;
-
- vfslocked = VFS_LOCK_GIANT(vp->v_mount);
- error = vn_lock(vp, LK_SHARED);
- if (error != 0) {
- VFS_UNLOCK_GIANT(vfslocked);
- goto done;
- }
- error = VOP_GETATTR(vp, &va, td->td_ucred);
- if (error != 0 || off >= va.va_size) {
- VOP_UNLOCK(vp, 0);
- VFS_UNLOCK_GIANT(vfslocked);
- goto done;
- }
- VFS_UNLOCK_GIANT(vfslocked);
-
- /*
- * Loop and construct maximum sized mbuf chain to be bulk
- * dumped into socket buffer.
- */
- while (space > loopbytes) {
- vm_pindex_t pindex;
- vm_offset_t pgoff;
- struct mbuf *m0;
-
- /*
- * Calculate the amount to transfer.
- * Not to exceed a page, the EOF,
- * or the passed in nbytes.
- */
- pgoff = (vm_offset_t)(off & PAGE_MASK);
- rem = va.va_size - uap->offset;
- if (uap->nbytes != 0)
- rem = omin(rem, uap->nbytes);
- rem -= fsbytes + loopbytes;
- xfsize = omin(PAGE_SIZE - pgoff, rem);
- xfsize = omin(space - loopbytes, xfsize);
- if (xfsize <= 0) {
- done = 1; /* all data sent */
- break;
- }
-
- /*
- * Attempt to look up the page. Allocate
- * if not found or wait and loop if busy.
- */
- pindex = OFF_TO_IDX(off);
- VM_OBJECT_LOCK(obj);
- pg = vm_page_grab(obj, pindex, VM_ALLOC_NOBUSY |
- VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_RETRY);
-
- /*
- * Check if page is valid for what we need,
- * otherwise initiate I/O.
- * If we already turned some pages into mbufs,
- * send them off before we come here again and
- * block.
- */
- if (pg->valid && vm_page_is_valid(pg, pgoff, xfsize))
- VM_OBJECT_UNLOCK(obj);
- else if (m != NULL)
- error = EAGAIN; /* send what we already got */
- else if (uap->flags & SF_NODISKIO)
- error = EBUSY;
- else {
- ssize_t resid;
-
- VM_OBJECT_UNLOCK(obj);
-
- /*
- * Get the page from backing store.
- * XXXMAC: Because we don't have fp->f_cred
- * here, we pass in NOCRED. This is probably
- * wrong, but is consistent with our original
- * implementation.
- */
- vfslocked = VFS_LOCK_GIANT(vp->v_mount);
- error = vn_rdwr(UIO_READ, vp, NULL, MAXBSIZE,
- trunc_page(off), UIO_NOCOPY, IO_NODELOCKED |
- IO_VMIO | ((MAXBSIZE / bsize) << IO_SEQSHIFT),
- td->td_ucred, NOCRED, &resid, td);
- VFS_UNLOCK_GIANT(vfslocked);
- if (error)
- VM_OBJECT_LOCK(obj);
- mbstat.sf_iocnt++;
- }
- if (error) {
- vm_page_lock(pg);
- vm_page_unwire(pg, 0);
- /*
- * See if anyone else might know about
- * this page. If not and it is not valid,
- * then free it.
- */
- if (pg->wire_count == 0 && pg->valid == 0 &&
- pg->busy == 0 && !(pg->oflags & VPO_BUSY))
- vm_page_free(pg);
- vm_page_unlock(pg);
- VM_OBJECT_UNLOCK(obj);
- if (error == EAGAIN)
- error = 0; /* not a real error */
- break;
- }
-
- /*
- * Get a sendfile buf. When allocating the
- * first buffer for mbuf chain, we usually
- * wait as long as necessary, but this wait
- * can be interrupted. For consequent
- * buffers, do not sleep, since several
- * threads might exhaust the buffers and then
- * deadlock.
- */
- sf = sf_buf_alloc(pg, (mnw || m != NULL) ? SFB_NOWAIT :
- SFB_CATCH);
- if (sf == NULL) {
- mbstat.sf_allocfail++;
- vm_page_lock(pg);
- vm_page_unwire(pg, 0);
- KASSERT(pg->object != NULL,
- ("kern_sendfile: object disappeared"));
- vm_page_unlock(pg);
- if (m == NULL)
- error = (mnw ? EAGAIN : EINTR);
- break;
- }
-
- /*
- * Get an mbuf and set it up as having
- * external storage.
- */
- m0 = m_get((mnw ? M_NOWAIT : M_WAITOK), MT_DATA);
- if (m0 == NULL) {
- error = (mnw ? EAGAIN : ENOBUFS);
- sf_buf_mext(NULL, sf);
- break;
- }
- MEXTADD(m0, sf_buf_kva(sf), PAGE_SIZE, sf_buf_mext,
- sfs, sf, M_RDONLY, EXT_SFBUF);
- m0->m_data = (char *)sf_buf_kva(sf) + pgoff;
- m0->m_len = xfsize;
-
- /* Append to mbuf chain. */
- if (mtail != NULL)
- mtail->m_next = m0;
- else if (m != NULL)
- m_last(m)->m_next = m0;
- else
- m = m0;
- mtail = m0;
-
- /* Keep track of bits processed. */
- loopbytes += xfsize;
- off += xfsize;
-
- if (sfs != NULL) {
- mtx_lock(&sfs->mtx);
- sfs->count++;
- mtx_unlock(&sfs->mtx);
- }
- }
-
- VOP_UNLOCK(vp, 0);
-
- /* Add the buffer chain to the socket buffer. */
- if (m != NULL) {
- int mlen, err;
-
- mlen = m_length(m, NULL);
- SOCKBUF_LOCK(&so->so_snd);
- if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
- error = EPIPE;
- SOCKBUF_UNLOCK(&so->so_snd);
- goto done;
- }
- SOCKBUF_UNLOCK(&so->so_snd);
- CURVNET_SET(so->so_vnet);
- /* Avoid error aliasing. */
- err = (*so->so_proto->pr_usrreqs->pru_send)
- (so, 0, m, NULL, NULL, td);
- CURVNET_RESTORE();
- if (err == 0) {
- /*
- * We need two counters to get the
- * file offset and nbytes to send
- * right:
- * - sbytes contains the total amount
- * of bytes sent, including headers.
- * - fsbytes contains the total amount
- * of bytes sent from the file.
- */
- sbytes += mlen;
- fsbytes += mlen;
- if (hdrlen) {
- fsbytes -= hdrlen;
- hdrlen = 0;
- }
- } else if (error == 0)
- error = err;
- m = NULL; /* pru_send always consumes */
- }
-
- /* Quit outer loop on error or when we're done. */
- if (done)
- break;
- if (error)
- goto done;
- }
-
- /*
- * Send trailers. Wimp out and use writev(2).
- */
- if (trl_uio != NULL) {
- sbunlock(&so->so_snd);
- error = kern_writev(td, uap->s, trl_uio);
- if (error == 0)
- sbytes += td->td_retval[0];
- goto out;
- }
-
-done:
- sbunlock(&so->so_snd);
-out:
- /*
- * If there was no error we have to clear td->td_retval[0]
- * because it may have been set by writev.
- */
- if (error == 0) {
- td->td_retval[0] = 0;
- }
- if (uap->sbytes != NULL) {
- copyout(&sbytes, uap->sbytes, sizeof(off_t));
- }
- if (obj != NULL)
- vm_object_deallocate(obj);
- if (vp != NULL) {
- vfslocked = VFS_LOCK_GIANT(vp->v_mount);
- vrele(vp);
- VFS_UNLOCK_GIANT(vfslocked);
- }
- if (so)
- fdrop(sock_fp, td);
- if (m)
- m_freem(m);
-
- if (sfs != NULL) {
- mtx_lock(&sfs->mtx);
- if (sfs->count != 0)
- cv_wait(&sfs->cv, &sfs->mtx);
- KASSERT(sfs->count == 0, ("sendfile sync still busy"));
- cv_destroy(&sfs->cv);
- mtx_destroy(&sfs->mtx);
- free(sfs, M_TEMP);
- }
-
- if (error == ERESTART)
- error = EINTR;
-
- return (error);
-}
-
-/*
- * SCTP syscalls.
- * Functionality only compiled in if SCTP is defined in the kernel Makefile,
- * otherwise all return EOPNOTSUPP.
- * XXX: We should make this loadable one day.
- */
-int
-sys_sctp_peeloff(td, uap)
- struct thread *td;
- struct sctp_peeloff_args /* {
- int sd;
- caddr_t name;
- } */ *uap;
-{
-#if (defined(INET) || defined(INET6)) && defined(SCTP)
- struct filedesc *fdp;
- struct file *nfp = NULL;
- int error;
- struct socket *head, *so;
- int fd;
- u_int fflag;
-
- fdp = td->td_proc->p_fd;
- AUDIT_ARG_FD(uap->sd);
- error = fgetsock(td, uap->sd, CAP_PEELOFF, &head, &fflag);
- if (error)
- goto done2;
- if (head->so_proto->pr_protocol != IPPROTO_SCTP) {
- error = EOPNOTSUPP;
- goto done;
- }
- error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name);
- if (error)
- goto done;
- /*
- * At this point we know we do have a assoc to pull
- * we proceed to get the fd setup. This may block
- * but that is ok.
- */
-
- error = falloc(td, &nfp, &fd, 0);
- if (error)
- goto done;
- td->td_retval[0] = fd;
-
- CURVNET_SET(head->so_vnet);
- so = sonewconn(head, SS_ISCONNECTED);
- if (so == NULL) {
- error = ENOMEM;
- goto noconnection;
- }
- /*
- * Before changing the flags on the socket, we have to bump the
- * reference count. Otherwise, if the protocol calls sofree(),
- * the socket will be released due to a zero refcount.
- */
- SOCK_LOCK(so);
- soref(so); /* file descriptor reference */
- SOCK_UNLOCK(so);
-
- ACCEPT_LOCK();
-
- TAILQ_REMOVE(&head->so_comp, so, so_list);
- head->so_qlen--;
- so->so_state |= (head->so_state & SS_NBIO);
- so->so_state &= ~SS_NOFDREF;
- so->so_qstate &= ~SQ_COMP;
- so->so_head = NULL;
- ACCEPT_UNLOCK();
- finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
- error = sctp_do_peeloff(head, so, (sctp_assoc_t)uap->name);
- if (error)
- goto noconnection;
- if (head->so_sigio != NULL)
- fsetown(fgetown(&head->so_sigio), &so->so_sigio);
-
-noconnection:
- /*
- * close the new descriptor, assuming someone hasn't ripped it
- * out from under us.
- */
- if (error)
- fdclose(fdp, nfp, fd, td);
-
- /*
- * Release explicitly held references before returning.
- */
- CURVNET_RESTORE();
-done:
- if (nfp != NULL)
- fdrop(nfp, td);
- fputsock(head);
-done2:
- return (error);
-#else /* SCTP */
- return (EOPNOTSUPP);
-#endif /* SCTP */
-}
-
-int
-sys_sctp_generic_sendmsg (td, uap)
- struct thread *td;
- struct sctp_generic_sendmsg_args /* {
- int sd,
- caddr_t msg,
- int mlen,
- caddr_t to,
- __socklen_t tolen,
- struct sctp_sndrcvinfo *sinfo,
- int flags
- } */ *uap;
-{
-#if (defined(INET) || defined(INET6)) && defined(SCTP)
- struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
- struct socket *so;
- struct file *fp = NULL;
- int error = 0, len;
- struct sockaddr *to = NULL;
-#ifdef KTRACE
- struct uio *ktruio = NULL;
-#endif
- struct uio auio;
- struct iovec iov[1];
- cap_rights_t rights;
-
- if (uap->sinfo) {
- error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
- if (error)
- return (error);
- u_sinfo = &sinfo;
- }
-
- rights = CAP_WRITE;
- if (uap->tolen) {
- error = getsockaddr(&to, uap->to, uap->tolen);
- if (error) {
- to = NULL;
- goto sctp_bad2;
- }
- rights |= CAP_CONNECT;
- }
-
- AUDIT_ARG_FD(uap->sd);
- error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL);
- if (error)
- goto sctp_bad;
-#ifdef KTRACE
- if (to && (KTRPOINT(td, KTR_STRUCT)))
- ktrsockaddr(to);
-#endif
-
- iov[0].iov_base = uap->msg;
- iov[0].iov_len = uap->mlen;
-
- so = (struct socket *)fp->f_data;
- if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
- error = EOPNOTSUPP;
- goto sctp_bad;
- }
-#ifdef MAC
- error = mac_socket_check_send(td->td_ucred, so);
- if (error)
- goto sctp_bad;
-#endif /* MAC */
-
- auio.uio_iov = iov;
- auio.uio_iovcnt = 1;
- auio.uio_segflg = UIO_USERSPACE;
- auio.uio_rw = UIO_WRITE;
- auio.uio_td = td;
- auio.uio_offset = 0; /* XXX */
- auio.uio_resid = 0;
- len = auio.uio_resid = uap->mlen;
- CURVNET_SET(so->so_vnet);
- error = sctp_lower_sosend(so, to, &auio,
- (struct mbuf *)NULL, (struct mbuf *)NULL,
- uap->flags, u_sinfo, td);
- CURVNET_RESTORE();
- if (error) {
- if (auio.uio_resid != len && (error == ERESTART ||
- error == EINTR || error == EWOULDBLOCK))
- error = 0;
- /* Generation of SIGPIPE can be controlled per socket. */
- if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
- !(uap->flags & MSG_NOSIGNAL)) {
- PROC_LOCK(td->td_proc);
- tdsignal(td, SIGPIPE);
- PROC_UNLOCK(td->td_proc);
- }
- }
- if (error == 0)
- td->td_retval[0] = len - auio.uio_resid;
-#ifdef KTRACE
- if (ktruio != NULL) {
- ktruio->uio_resid = td->td_retval[0];
- ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
- }
-#endif /* KTRACE */
-sctp_bad:
- if (fp)
- fdrop(fp, td);
-sctp_bad2:
- if (to)
- free(to, M_SONAME);
- return (error);
-#else /* SCTP */
- return (EOPNOTSUPP);
-#endif /* SCTP */
-}
-
-int
-sys_sctp_generic_sendmsg_iov(td, uap)
- struct thread *td;
- struct sctp_generic_sendmsg_iov_args /* {
- int sd,
- struct iovec *iov,
- int iovlen,
- caddr_t to,
- __socklen_t tolen,
- struct sctp_sndrcvinfo *sinfo,
- int flags
- } */ *uap;
-{
-#if (defined(INET) || defined(INET6)) && defined(SCTP)
- struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL;
- struct socket *so;
- struct file *fp = NULL;
- int error=0, i;
- ssize_t len;
- struct sockaddr *to = NULL;
-#ifdef KTRACE
- struct uio *ktruio = NULL;
-#endif
- struct uio auio;
- struct iovec *iov, *tiov;
- cap_rights_t rights;
-
- if (uap->sinfo) {
- error = copyin(uap->sinfo, &sinfo, sizeof (sinfo));
- if (error)
- return (error);
- u_sinfo = &sinfo;
- }
- rights = CAP_WRITE;
- if (uap->tolen) {
- error = getsockaddr(&to, uap->to, uap->tolen);
- if (error) {
- to = NULL;
- goto sctp_bad2;
- }
- rights |= CAP_CONNECT;
- }
-
- AUDIT_ARG_FD(uap->sd);
- error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL);
- if (error)
- goto sctp_bad1;
-
-#ifdef COMPAT_FREEBSD32
- if (SV_CURPROC_FLAG(SV_ILP32))
- error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
- uap->iovlen, &iov, EMSGSIZE);
- else
-#endif
- error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
- if (error)
- goto sctp_bad1;
-#ifdef KTRACE
- if (to && (KTRPOINT(td, KTR_STRUCT)))
- ktrsockaddr(to);
-#endif
-
- so = (struct socket *)fp->f_data;
- if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
- error = EOPNOTSUPP;
- goto sctp_bad;
- }
-#ifdef MAC
- error = mac_socket_check_send(td->td_ucred, so);
- if (error)
- goto sctp_bad;
-#endif /* MAC */
-
- auio.uio_iov = iov;
- auio.uio_iovcnt = uap->iovlen;
- auio.uio_segflg = UIO_USERSPACE;
- auio.uio_rw = UIO_WRITE;
- auio.uio_td = td;
- auio.uio_offset = 0; /* XXX */
- auio.uio_resid = 0;
- tiov = iov;
- for (i = 0; i <uap->iovlen; i++, tiov++) {
- if ((auio.uio_resid += tiov->iov_len) < 0) {
- error = EINVAL;
- goto sctp_bad;
- }
- }
- len = auio.uio_resid;
- CURVNET_SET(so->so_vnet);
- error = sctp_lower_sosend(so, to, &auio,
- (struct mbuf *)NULL, (struct mbuf *)NULL,
- uap->flags, u_sinfo, td);
- CURVNET_RESTORE();
- if (error) {
- if (auio.uio_resid != len && (error == ERESTART ||
- error == EINTR || error == EWOULDBLOCK))
- error = 0;
- /* Generation of SIGPIPE can be controlled per socket */
- if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
- !(uap->flags & MSG_NOSIGNAL)) {
- PROC_LOCK(td->td_proc);
- tdsignal(td, SIGPIPE);
- PROC_UNLOCK(td->td_proc);
- }
- }
- if (error == 0)
- td->td_retval[0] = len - auio.uio_resid;
-#ifdef KTRACE
- if (ktruio != NULL) {
- ktruio->uio_resid = td->td_retval[0];
- ktrgenio(uap->sd, UIO_WRITE, ktruio, error);
- }
-#endif /* KTRACE */
-sctp_bad:
- free(iov, M_IOV);
-sctp_bad1:
- if (fp)
- fdrop(fp, td);
-sctp_bad2:
- if (to)
- free(to, M_SONAME);
- return (error);
-#else /* SCTP */
- return (EOPNOTSUPP);
-#endif /* SCTP */
-}
-
-int
-sys_sctp_generic_recvmsg(td, uap)
- struct thread *td;
- struct sctp_generic_recvmsg_args /* {
- int sd,
- struct iovec *iov,
- int iovlen,
- struct sockaddr *from,
- __socklen_t *fromlenaddr,
- struct sctp_sndrcvinfo *sinfo,
- int *msg_flags
- } */ *uap;
-{
-#if (defined(INET) || defined(INET6)) && defined(SCTP)
- uint8_t sockbufstore[256];
- struct uio auio;
- struct iovec *iov, *tiov;
- struct sctp_sndrcvinfo sinfo;
- struct socket *so;
- struct file *fp = NULL;
- struct sockaddr *fromsa;
- int fromlen;
- ssize_t len;
- int i, msg_flags;
- int error = 0;
-#ifdef KTRACE
- struct uio *ktruio = NULL;
-#endif
-
- AUDIT_ARG_FD(uap->sd);
- error = getsock_cap(td->td_proc->p_fd, uap->sd, CAP_READ, &fp, NULL);
- if (error) {
- return (error);
- }
-#ifdef COMPAT_FREEBSD32
- if (SV_CURPROC_FLAG(SV_ILP32))
- error = freebsd32_copyiniov((struct iovec32 *)uap->iov,
- uap->iovlen, &iov, EMSGSIZE);
- else
-#endif
- error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
- if (error)
- goto out1;
-
- so = fp->f_data;
- if (so->so_proto->pr_protocol != IPPROTO_SCTP) {
- error = EOPNOTSUPP;
- goto out;
- }
-#ifdef MAC
- error = mac_socket_check_receive(td->td_ucred, so);
- if (error) {
- goto out;
- }
-#endif /* MAC */
-
- if (uap->fromlenaddr) {
- error = copyin(uap->fromlenaddr,
- &fromlen, sizeof (fromlen));
- if (error) {
- goto out;
- }
- } else {
- fromlen = 0;
- }
- if (uap->msg_flags) {
- error = copyin(uap->msg_flags, &msg_flags, sizeof (int));
- if (error) {
- goto out;
- }
- } else {
- msg_flags = 0;
- }
- auio.uio_iov = iov;
- auio.uio_iovcnt = uap->iovlen;
- auio.uio_segflg = UIO_USERSPACE;
- auio.uio_rw = UIO_READ;
- auio.uio_td = td;
- auio.uio_offset = 0; /* XXX */
- auio.uio_resid = 0;
- tiov = iov;
- for (i = 0; i <uap->iovlen; i++, tiov++) {
- if ((auio.uio_resid += tiov->iov_len) < 0) {
- error = EINVAL;
- goto out;
- }
- }
- len = auio.uio_resid;
- fromsa = (struct sockaddr *)sockbufstore;
-
-#ifdef KTRACE
- if (KTRPOINT(td, KTR_GENIO))
- ktruio = cloneuio(&auio);
-#endif /* KTRACE */
- memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
- CURVNET_SET(so->so_vnet);
- error = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL,
- fromsa, fromlen, &msg_flags,
- (struct sctp_sndrcvinfo *)&sinfo, 1);
- CURVNET_RESTORE();
- if (error) {
- if (auio.uio_resid != len && (error == ERESTART ||
- error == EINTR || error == EWOULDBLOCK))
- error = 0;
- } else {
- if (uap->sinfo)
- error = copyout(&sinfo, uap->sinfo, sizeof (sinfo));
- }
-#ifdef KTRACE
- if (ktruio != NULL) {
- ktruio->uio_resid = len - auio.uio_resid;
- ktrgenio(uap->sd, UIO_READ, ktruio, error);
- }
-#endif /* KTRACE */
- if (error)
- goto out;
- td->td_retval[0] = len - auio.uio_resid;
-
- if (fromlen && uap->from) {
- len = fromlen;
- if (len <= 0 || fromsa == 0)
- len = 0;
- else {
- len = MIN(len, fromsa->sa_len);
- error = copyout(fromsa, uap->from, (size_t)len);
- if (error)
- goto out;
- }
- error = copyout(&len, uap->fromlenaddr, sizeof (socklen_t));
- if (error) {
- goto out;
- }
- }
-#ifdef KTRACE
- if (KTRPOINT(td, KTR_STRUCT))
- ktrsockaddr(fromsa);
-#endif
- if (uap->msg_flags) {
- error = copyout(&msg_flags, uap->msg_flags, sizeof (int));
- if (error) {
- goto out;
- }
- }
-out:
- free(iov, M_IOV);
-out1:
- if (fp)
- fdrop(fp, td);
-
- return (error);
-#else /* SCTP */
- return (EOPNOTSUPP);
-#endif /* SCTP */
-}
-#endif /* __rtems__ */