diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-10-07 15:10:20 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-01-10 09:53:31 +0100 |
commit | c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f (patch) | |
tree | ad4f2519067709f00ab98b3c591186c26dc3a21f /freebsd/sys/kern/uipc_syscalls.c | |
parent | userspace-header-gen.py: Simplify program ports (diff) | |
download | rtems-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.c | 1717 |
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__ */ |