#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
#include <sys/flock.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/vmsystm.h>
#include <sys/policy.h>
#include <sys/limits.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/isa_defs.h>
#include <sys/inttypes.h>
#include <sys/systm.h>
#include <sys/cpuvar.h>
#include <sys/filio.h>
#include <sys/sendfile.h>
#include <sys/ddi.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/seg_kpm.h>
#include <fs/sockfs/sockcommon.h>
#include <fs/sockfs/sockfilter_impl.h>
#include <fs/sockfs/socktpi.h>
#ifdef SOCK_TEST
int do_useracc = 1;
#else
#define do_useracc 1
#endif
extern int xnet_truncate_print;
#define SOCK_KNOWN_FLAGS (SOCK_CLOEXEC | SOCK_NDELAY | SOCK_NONBLOCK | \
SOCK_CLOFORK)
int
so_socket(int family, int type_w_flags, int protocol, char *devpath,
int version)
{
struct sonode *so;
vnode_t *vp;
struct file *fp;
int fd;
int error;
int type;
type = type_w_flags & SOCK_TYPE_MASK;
type_w_flags &= ~SOCK_TYPE_MASK;
if (type_w_flags & ~SOCK_KNOWN_FLAGS)
return (set_errno(EINVAL));
if (devpath != NULL) {
char *buf;
size_t kdevpathlen = 0;
buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
if ((error = copyinstr(devpath, buf,
MAXPATHLEN, &kdevpathlen)) != 0) {
kmem_free(buf, MAXPATHLEN);
return (set_errno(error));
}
so = socket_create(family, type, protocol, buf, NULL,
SOCKET_SLEEP, version, CRED(), &error);
kmem_free(buf, MAXPATHLEN);
} else {
so = socket_create(family, type, protocol, NULL, NULL,
SOCKET_SLEEP, version, CRED(), &error);
}
if (so == NULL)
return (set_errno(error));
vp = SOTOV(so);
error = falloc(vp, FWRITE|FREAD, &fp, &fd);
if (error != 0) {
(void) socket_close(so, 0, CRED());
socket_destroy(so);
return (set_errno(error));
}
if (type_w_flags & SOCK_NDELAY) {
so->so_state |= SS_NDELAY;
fp->f_flag |= FNDELAY;
}
if (type_w_flags & SOCK_NONBLOCK) {
so->so_state |= SS_NONBLOCK;
fp->f_flag |= FNONBLOCK;
}
mutex_exit(&fp->f_tlock);
setf(fd, fp);
if ((type_w_flags & SOCK_CLOEXEC) != 0) {
f_setfd_or(fd, FD_CLOEXEC);
}
if ((type_w_flags & SOCK_CLOFORK) != 0) {
f_setfd_or(fd, FD_CLOFORK);
}
return (fd);
}
struct sonode *
getsonode(int sock, int *errorp, file_t **fpp)
{
file_t *fp;
vnode_t *vp;
struct sonode *so;
if ((fp = getf(sock)) == NULL) {
*errorp = EBADF;
eprintline(*errorp);
return (NULL);
}
vp = fp->f_vnode;
if (vp->v_type != VSOCK) {
releasef(sock);
*errorp = ENOTSOCK;
eprintline(*errorp);
return (NULL);
}
if (vp->v_stream) {
ASSERT(vp->v_stream->sd_vnode);
vp = vp->v_stream->sd_vnode;
so = VTOSO(vp);
if (so->so_version == SOV_STREAM) {
releasef(sock);
*errorp = ENOTSOCK;
eprintsoline(so, *errorp);
return (NULL);
}
} else {
so = VTOSO(vp);
}
if (fpp)
*fpp = fp;
return (so);
}
static struct sockaddr *
copyin_name(struct sonode *so, struct sockaddr *name, socklen_t *namelenp,
int *errorp)
{
char *faddr;
size_t namelen = (size_t)*namelenp;
ASSERT(namelen != 0);
if (namelen > SO_MAXARGSIZE) {
*errorp = EINVAL;
eprintsoline(so, *errorp);
return (NULL);
}
faddr = (char *)kmem_alloc(namelen, KM_SLEEP);
if (copyin(name, faddr, namelen)) {
kmem_free(faddr, namelen);
*errorp = EFAULT;
eprintsoline(so, *errorp);
return (NULL);
}
if (so->so_family == AF_UNIX && faddr[namelen - 1] != '\0') {
size_t i;
int foundnull = 0;
for (i = sizeof (name->sa_family); i < namelen; i++) {
if (faddr[i] == '\0') {
foundnull = 1;
break;
}
}
if (!foundnull) {
char *nfaddr;
nfaddr = (char *)kmem_alloc(namelen + 1, KM_SLEEP);
bcopy(faddr, nfaddr, namelen);
kmem_free(faddr, namelen);
nfaddr[namelen] = '\0';
namelen++;
ASSERT((socklen_t)namelen == namelen);
*namelenp = (socklen_t)namelen;
faddr = nfaddr;
}
}
return ((struct sockaddr *)faddr);
}
static int
copyout_arg(void *uaddr, socklen_t ulen, void *ulenp, void *kaddr,
socklen_t klen)
{
if (uaddr != NULL) {
if (ulen > klen)
ulen = klen;
if (ulen != 0) {
if (copyout(kaddr, uaddr, ulen))
return (EFAULT);
}
} else
ulen = 0;
if (ulenp != NULL) {
if (copyout(&ulen, ulenp, sizeof (ulen)))
return (EFAULT);
}
return (0);
}
static int
copyout_name(void *uaddr, socklen_t ulen, void *ulenp, void *kaddr,
socklen_t klen)
{
if (uaddr != NULL) {
if (ulen >= klen)
ulen = klen;
else if (ulen != 0 && xnet_truncate_print) {
printf("sockfs: truncating copyout of address using "
"XNET semantics for pid = %d. Lengths %d, %d\n",
curproc->p_pid, klen, ulen);
}
if (ulen != 0) {
if (copyout(kaddr, uaddr, ulen))
return (EFAULT);
} else
klen = 0;
} else
klen = 0;
if (ulenp != NULL) {
if (copyout(&klen, ulenp, sizeof (klen)))
return (EFAULT);
}
return (0);
}
int
so_socketpair(int sv[2])
{
int svs[2];
struct sonode *so1, *so2;
int error;
int orig_flags;
struct sockaddr_ux *name;
size_t namelen;
sotpi_info_t *sti1;
sotpi_info_t *sti2;
dprint(1, ("so_socketpair(%p)\n", (void *)sv));
error = useracc(sv, sizeof (svs), B_WRITE);
if (error && do_useracc)
return (set_errno(EFAULT));
if (copyin(sv, svs, sizeof (svs)))
return (set_errno(EFAULT));
if ((so1 = getsonode(svs[0], &error, NULL)) == NULL)
return (set_errno(error));
if ((so2 = getsonode(svs[1], &error, NULL)) == NULL) {
releasef(svs[0]);
return (set_errno(error));
}
if (so1->so_family != AF_UNIX || so2->so_family != AF_UNIX) {
error = EOPNOTSUPP;
goto done;
}
sti1 = SOTOTPI(so1);
sti2 = SOTOTPI(so2);
ASSERT(so1->so_ops == &sotpi_sonodeops);
ASSERT(so2->so_ops == &sotpi_sonodeops);
if (so1->so_type == SOCK_DGRAM) {
error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC, CRED());
if (error) {
eprintsoline(so1, error);
goto done;
}
error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
if (error) {
eprintsoline(so2, error);
goto done;
}
namelen = sizeof (struct sockaddr_ux);
name = kmem_alloc(namelen, KM_SLEEP);
name->sou_family = AF_UNIX;
name->sou_addr = sti2->sti_ux_laddr;
error = socket_connect(so1,
(struct sockaddr *)name,
(socklen_t)namelen,
0, _SOCONNECT_NOXLATE, CRED());
if (error) {
kmem_free(name, namelen);
eprintsoline(so1, error);
goto done;
}
name->sou_addr = sti1->sti_ux_laddr;
error = socket_connect(so2,
(struct sockaddr *)name,
(socklen_t)namelen,
0, _SOCONNECT_NOXLATE, CRED());
kmem_free(name, namelen);
if (error) {
eprintsoline(so2, error);
goto done;
}
releasef(svs[0]);
releasef(svs[1]);
} else {
struct sonode *nso;
struct vnode *nvp;
struct file *nfp;
int nfd;
error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC|
_SOBIND_NOXLATE|_SOBIND_LISTEN|_SOBIND_SOCKETPAIR,
CRED());
if (error) {
eprintsoline(so1, error);
goto done;
}
error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
if (error) {
eprintsoline(so2, error);
goto done;
}
namelen = sizeof (struct sockaddr_ux);
name = kmem_alloc(namelen, KM_SLEEP);
name->sou_family = AF_UNIX;
name->sou_addr = sti1->sti_ux_laddr;
error = socket_connect(so2,
(struct sockaddr *)name,
(socklen_t)namelen,
FNONBLOCK, _SOCONNECT_NOXLATE, CRED());
kmem_free(name, namelen);
if (error) {
if (error != EINPROGRESS) {
eprintsoline(so2, error); goto done;
}
}
error = socket_accept(so1, 0, CRED(), &nso);
if (error) {
eprintsoline(so1, error);
goto done;
}
mutex_enter(&so2->so_lock);
error = sowaitconnected(so2, 0, 1);
mutex_exit(&so2->so_lock);
if (error != 0) {
(void) socket_close(nso, 0, CRED());
socket_destroy(nso);
eprintsoline(so2, error);
goto done;
}
nvp = SOTOV(nso);
error = falloc(nvp, FWRITE|FREAD, &nfp, &nfd);
if (error != 0) {
(void) socket_close(nso, 0, CRED());
socket_destroy(nso);
eprintsoline(nso, error);
goto done;
}
if (so1->so_state & SS_NONBLOCK)
nfp->f_flag |= FNONBLOCK;
if (so1->so_state & SS_NDELAY)
nfp->f_flag |= FNDELAY;
mutex_exit(&nfp->f_tlock);
setf(nfd, nfp);
VERIFY(f_getfd_error(svs[0], &orig_flags) == 0);
releasef(svs[0]);
releasef(svs[1]);
if (orig_flags & (FD_CLOEXEC | FD_CLOFORK)) {
f_setfd_or(nfd, orig_flags & (FD_CLOEXEC | FD_CLOFORK));
}
svs[0] = nfd;
if (copyout(svs, sv, sizeof (svs))) {
(void) closeandsetf(nfd, NULL);
eprintline(EFAULT);
return (set_errno(EFAULT));
}
}
return (0);
done:
releasef(svs[0]);
releasef(svs[1]);
return (set_errno(error));
}
int
bind(int sock, struct sockaddr *name, socklen_t namelen, int version)
{
struct sonode *so;
int error;
dprint(1, ("bind(%d, %p, %d)\n",
sock, (void *)name, namelen));
if ((so = getsonode(sock, &error, NULL)) == NULL)
return (set_errno(error));
if (name != NULL && namelen != 0) {
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
name = copyin_name(so, name, &namelen, &error);
if (name == NULL) {
releasef(sock);
return (set_errno(error));
}
} else {
name = NULL;
namelen = 0;
}
switch (version) {
default:
error = socket_bind(so, name, namelen, 0, CRED());
break;
case SOV_XPG4_2:
error = socket_bind(so, name, namelen, _SOBIND_XPG4_2, CRED());
break;
case SOV_SOCKBSD:
error = socket_bind(so, name, namelen, _SOBIND_SOCKBSD, CRED());
break;
}
releasef(sock);
if (name != NULL)
kmem_free(name, (size_t)namelen);
if (error)
return (set_errno(error));
return (0);
}
int
listen(int sock, int backlog, int version)
{
struct sonode *so;
int error;
dprint(1, ("listen(%d, %d)\n",
sock, backlog));
if ((so = getsonode(sock, &error, NULL)) == NULL)
return (set_errno(error));
error = socket_listen(so, backlog, CRED());
releasef(sock);
if (error)
return (set_errno(error));
return (0);
}
int
accept(int sock, struct sockaddr *name, socklen_t *namelenp, int version,
int flags)
{
struct sonode *so;
file_t *fp;
int error;
socklen_t namelen;
struct sonode *nso;
struct vnode *nvp;
struct file *nfp;
int nfd;
int ssflags;
struct sockaddr *addrp;
socklen_t addrlen;
dprint(1, ("accept(%d, %p, %p)\n",
sock, (void *)name, (void *)namelenp));
if (flags & ~SOCK_KNOWN_FLAGS) {
return (set_errno(EINVAL));
}
ssflags = 0;
if (flags & SOCK_NONBLOCK)
ssflags |= SS_NONBLOCK;
if (flags & SOCK_NDELAY)
ssflags |= SS_NDELAY;
if ((so = getsonode(sock, &error, &fp)) == NULL)
return (set_errno(error));
if (name != NULL) {
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(namelenp, &namelen, sizeof (namelen))) {
releasef(sock);
return (set_errno(EFAULT));
}
if (namelen != 0) {
error = useracc(name, (size_t)namelen, B_WRITE);
if (error && do_useracc) {
releasef(sock);
return (set_errno(EFAULT));
}
} else
name = NULL;
} else {
namelen = 0;
}
if ((nfd = ufalloc(0)) == -1) {
eprintsoline(so, EMFILE);
releasef(sock);
return (set_errno(EMFILE));
}
error = socket_accept(so, fp->f_flag, CRED(), &nso);
if (error) {
setf(nfd, NULL);
releasef(sock);
return (set_errno(error));
}
nvp = SOTOV(nso);
ASSERT(MUTEX_NOT_HELD(&nso->so_lock));
if (namelen != 0) {
addrlen = so->so_max_addr_len;
addrp = (struct sockaddr *)kmem_alloc(addrlen, KM_SLEEP);
if ((error = socket_getpeername(nso, (struct sockaddr *)addrp,
&addrlen, B_TRUE, CRED())) == 0) {
error = copyout_name(name, namelen, namelenp,
addrp, addrlen);
} else {
ASSERT(error == EINVAL || error == ENOTCONN);
error = ECONNABORTED;
}
kmem_free(addrp, so->so_max_addr_len);
}
if (error) {
setf(nfd, NULL);
(void) socket_close(nso, 0, CRED());
socket_destroy(nso);
releasef(sock);
return (set_errno(error));
}
error = falloc(NULL, FWRITE|FREAD, &nfp, NULL);
if (error != 0) {
setf(nfd, NULL);
(void) socket_close(nso, 0, CRED());
socket_destroy(nso);
eprintsoline(so, error);
releasef(sock);
return (set_errno(error));
}
nfp->f_vnode = nvp;
mutex_exit(&nfp->f_tlock);
setf(nfd, nfp);
if (flags & SOCK_CLOEXEC) {
f_setfd_or(nfd, FD_CLOEXEC);
}
if (flags & SOCK_CLOFORK) {
f_setfd_or(nfd, FD_CLOFORK);
}
if ((ssflags | so->so_state) & (SS_NDELAY|SS_NONBLOCK)) {
uint_t oflag = nfp->f_flag;
int arg = 0;
if ((ssflags | so->so_state) & SS_NONBLOCK)
arg |= FNONBLOCK;
else if ((ssflags | so->so_state) & SS_NDELAY)
arg |= FNDELAY;
if ((error = VOP_SETFL(nvp, oflag, arg, nfp->f_cred, NULL))
!= 0) {
eprintsoline(so, error);
error = 0;
} else {
mutex_enter(&nfp->f_tlock);
nfp->f_flag &= ~FMASK | (FREAD|FWRITE);
nfp->f_flag |= arg;
mutex_exit(&nfp->f_tlock);
}
}
releasef(sock);
return (nfd);
}
int
connect(int sock, struct sockaddr *name, socklen_t namelen, int version)
{
struct sonode *so;
file_t *fp;
int error;
dprint(1, ("connect(%d, %p, %d)\n",
sock, (void *)name, namelen));
if ((so = getsonode(sock, &error, &fp)) == NULL)
return (set_errno(error));
if (namelen != 0) {
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
name = copyin_name(so, name, &namelen, &error);
if (name == NULL) {
releasef(sock);
return (set_errno(error));
}
} else
name = NULL;
error = socket_connect(so, name, namelen, fp->f_flag,
(version != SOV_XPG4_2) ? 0 : _SOCONNECT_XPG4_2, CRED());
releasef(sock);
if (name)
kmem_free(name, (size_t)namelen);
if (error)
return (set_errno(error));
return (0);
}
int
shutdown(int sock, int how, int version)
{
struct sonode *so;
int error;
dprint(1, ("shutdown(%d, %d)\n",
sock, how));
if ((so = getsonode(sock, &error, NULL)) == NULL)
return (set_errno(error));
error = socket_shutdown(so, how, CRED());
releasef(sock);
if (error)
return (set_errno(error));
return (0);
}
static ssize_t
recvit(int sock, struct nmsghdr *msg, struct uio *uiop, int flags,
socklen_t *namelenp, socklen_t *controllenp, int *flagsp)
{
struct sonode *so;
file_t *fp;
void *name;
socklen_t namelen;
void *control;
socklen_t controllen, free_controllen;
ssize_t len;
int error;
if ((so = getsonode(sock, &error, &fp)) == NULL)
return (set_errno(error));
len = uiop->uio_resid;
uiop->uio_fmode = fp->f_flag;
uiop->uio_extflg = UIO_COPY_CACHED;
name = msg->msg_name;
namelen = msg->msg_namelen;
control = msg->msg_control;
controllen = msg->msg_controllen;
msg->msg_flags = flags & (MSG_OOB | MSG_PEEK | MSG_WAITALL |
MSG_DONTWAIT | MSG_XPG4_2 | MSG_CMSG_CLOEXEC | MSG_CMSG_CLOFORK);
error = socket_recvmsg(so, msg, uiop, CRED());
if (error) {
releasef(sock);
return (set_errno(error));
}
lwp_stat_update(LWP_STAT_MSGRCV, 1);
releasef(sock);
free_controllen = msg->msg_controllen;
error = copyout_name(name, namelen, namelenp,
msg->msg_name, msg->msg_namelen);
if (error)
goto err;
if (flagsp != NULL) {
msg->msg_flags &= ~(MSG_XPG4_2 | MSG_CMSG_CLOEXEC |
MSG_CMSG_CLOFORK);
if (controllen != 0 &&
(msg->msg_controllen > controllen || control == NULL)) {
dprint(1, ("recvit: CTRUNC %d %d %p\n",
msg->msg_controllen, controllen, control));
msg->msg_flags |= MSG_CTRUNC;
}
if (copyout(&msg->msg_flags, flagsp,
sizeof (msg->msg_flags))) {
error = EFAULT;
goto err;
}
}
if (controllen != 0) {
if (!(flags & MSG_XPG4_2)) {
controllen &= ~((int)sizeof (uint32_t) - 1);
}
if (msg->msg_controllen > controllen || control == NULL) {
so_closefds(msg->msg_control, msg->msg_controllen,
!(flags & MSG_XPG4_2),
control == NULL ? 0 : controllen);
if (control != NULL && (flags & MSG_XPG4_2)) {
so_truncatecmsg(msg->msg_control,
msg->msg_controllen, controllen);
msg->msg_controllen = controllen;
}
}
error = copyout_arg(control, controllen, controllenp,
msg->msg_control, msg->msg_controllen);
if (error)
goto err;
}
if (msg->msg_namelen != 0)
kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
if (free_controllen != 0)
kmem_free(msg->msg_control, (size_t)free_controllen);
return (len - uiop->uio_resid);
err:
if (msg->msg_controllen != 0)
so_closefds(msg->msg_control, msg->msg_controllen,
!(flags & MSG_XPG4_2), 0);
if (msg->msg_namelen != 0)
kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
if (free_controllen != 0)
kmem_free(msg->msg_control, (size_t)free_controllen);
return (set_errno(error));
}
ssize_t
recv(int sock, void *buffer, size_t len, int flags)
{
struct nmsghdr lmsg;
struct uio auio;
struct iovec aiov[1];
dprint(1, ("recv(%d, %p, %ld, %d)\n",
sock, buffer, len, flags));
if ((ssize_t)len < 0) {
return (set_errno(EINVAL));
}
aiov[0].iov_base = buffer;
aiov[0].iov_len = len;
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
lmsg.msg_namelen = 0;
lmsg.msg_controllen = 0;
lmsg.msg_flags = 0;
return (recvit(sock, &lmsg, &auio, flags, NULL, NULL, NULL));
}
ssize_t
recvfrom(int sock, void *buffer, size_t len, int flags, struct sockaddr *name,
socklen_t *namelenp)
{
struct nmsghdr lmsg;
struct uio auio;
struct iovec aiov[1];
dprint(1, ("recvfrom(%d, %p, %ld, %d, %p, %p)\n",
sock, buffer, len, flags, (void *)name, (void *)namelenp));
if ((ssize_t)len < 0) {
return (set_errno(EINVAL));
}
aiov[0].iov_base = buffer;
aiov[0].iov_len = len;
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
lmsg.msg_name = (char *)name;
if (namelenp != NULL) {
if (copyin(namelenp, &lmsg.msg_namelen,
sizeof (lmsg.msg_namelen)))
return (set_errno(EFAULT));
} else {
lmsg.msg_namelen = 0;
}
lmsg.msg_controllen = 0;
lmsg.msg_flags = 0;
return (recvit(sock, &lmsg, &auio, flags, namelenp, NULL, NULL));
}
ssize_t
recvmsg(int sock, struct nmsghdr *msg, int flags)
{
STRUCT_DECL(nmsghdr, u_lmsg);
STRUCT_HANDLE(nmsghdr, umsgptr);
struct nmsghdr lmsg;
struct uio auio;
struct iovec buf[IOV_MAX_STACK], *aiov = buf;
ssize_t iovsize = 0;
int iovcnt;
ssize_t len, rval;
int i;
int *flagsp;
model_t model;
dprint(1, ("recvmsg(%d, %p, %d)\n",
sock, (void *)msg, flags));
model = get_udatamodel();
STRUCT_INIT(u_lmsg, model);
STRUCT_SET_HANDLE(umsgptr, model, msg);
if (flags & MSG_XPG4_2) {
if (copyin(msg, STRUCT_BUF(u_lmsg), STRUCT_SIZE(u_lmsg)))
return (set_errno(EFAULT));
flagsp = STRUCT_FADDR(umsgptr, msg_flags);
} else {
if (copyin(msg, STRUCT_BUF(u_lmsg),
SIZEOF_STRUCT(omsghdr, model)))
return (set_errno(EFAULT));
STRUCT_FSET(u_lmsg, msg_flags, 0);
flagsp = NULL;
}
lmsg.msg_name = STRUCT_FGETP(u_lmsg, msg_name);
lmsg.msg_namelen = STRUCT_FGET(u_lmsg, msg_namelen);
lmsg.msg_iov = STRUCT_FGETP(u_lmsg, msg_iov);
lmsg.msg_iovlen = STRUCT_FGET(u_lmsg, msg_iovlen);
lmsg.msg_control = STRUCT_FGETP(u_lmsg, msg_control);
lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
iovcnt = lmsg.msg_iovlen;
if (iovcnt <= 0 || iovcnt > IOV_MAX) {
return (set_errno(EMSGSIZE));
}
if (iovcnt > IOV_MAX_STACK) {
iovsize = iovcnt * sizeof (struct iovec);
aiov = kmem_alloc(iovsize, KM_SLEEP);
}
#ifdef _SYSCALL32_IMPL
if (model == DATAMODEL_ILP32) {
struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
ssize_t iov32size;
ssize32_t count32;
iov32size = iovcnt * sizeof (struct iovec32);
if (iovsize != 0)
aiov32 = kmem_alloc(iov32size, KM_SLEEP);
if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
if (iovsize != 0) {
kmem_free(aiov32, iov32size);
kmem_free(aiov, iovsize);
}
return (set_errno(EFAULT));
}
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen32;
iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
if (iovlen32 < 0 || count32 < 0) {
if (iovsize != 0) {
kmem_free(aiov32, iov32size);
kmem_free(aiov, iovsize);
}
return (set_errno(EINVAL));
}
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
if (iovsize != 0)
kmem_free(aiov32, iov32size);
} else
#endif
if (copyin(lmsg.msg_iov, aiov, iovcnt * sizeof (struct iovec))) {
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (set_errno(EFAULT));
}
len = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
len += iovlen;
if (iovlen < 0 || len < 0) {
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (set_errno(EINVAL));
}
}
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
if (lmsg.msg_control != NULL &&
(do_useracc == 0 ||
useracc(lmsg.msg_control, lmsg.msg_controllen,
B_WRITE) != 0)) {
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (set_errno(EFAULT));
}
rval = recvit(sock, &lmsg, &auio, flags,
STRUCT_FADDR(umsgptr, msg_namelen),
STRUCT_FADDR(umsgptr, msg_controllen), flagsp);
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (rval);
}
static ssize_t
sendit(int sock, struct nmsghdr *msg, struct uio *uiop, int flags)
{
struct sonode *so;
file_t *fp;
void *name;
socklen_t namelen;
void *control;
socklen_t controllen;
ssize_t len;
int error;
if ((so = getsonode(sock, &error, &fp)) == NULL)
return (set_errno(error));
uiop->uio_fmode = fp->f_flag;
if (so->so_family == AF_UNIX)
uiop->uio_extflg = UIO_COPY_CACHED;
else
uiop->uio_extflg = UIO_COPY_DEFAULT;
len = uiop->uio_resid;
name = msg->msg_name;
namelen = msg->msg_namelen;
if (name != NULL && namelen != 0) {
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
name = copyin_name(so,
(struct sockaddr *)name,
&namelen, &error);
if (name == NULL)
goto done3;
msg->msg_namelen = namelen;
msg->msg_name = name;
} else {
msg->msg_name = name = NULL;
msg->msg_namelen = namelen = 0;
}
control = msg->msg_control;
controllen = msg->msg_controllen;
if ((control != NULL) && (controllen != 0)) {
if (controllen > SO_MAXARGSIZE) {
error = EINVAL;
goto done2;
}
control = kmem_alloc(controllen, KM_SLEEP);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(msg->msg_control, control, controllen)) {
error = EFAULT;
goto done1;
}
msg->msg_control = control;
} else {
msg->msg_control = control = NULL;
msg->msg_controllen = controllen = 0;
}
msg->msg_flags = flags;
error = socket_sendmsg(so, msg, uiop, CRED());
done1:
if (control != NULL)
kmem_free(control, controllen);
done2:
if (name != NULL)
kmem_free(name, namelen);
done3:
if (error != 0) {
releasef(sock);
return (set_errno(error));
}
lwp_stat_update(LWP_STAT_MSGSND, 1);
releasef(sock);
return (len - uiop->uio_resid);
}
ssize_t
send(int sock, void *buffer, size_t len, int flags)
{
struct nmsghdr lmsg;
struct uio auio;
struct iovec aiov[1];
dprint(1, ("send(%d, %p, %ld, %d)\n",
sock, buffer, len, flags));
if ((ssize_t)len < 0) {
return (set_errno(EINVAL));
}
aiov[0].iov_base = buffer;
aiov[0].iov_len = len;
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
lmsg.msg_name = NULL;
lmsg.msg_control = NULL;
if (!(flags & MSG_XPG4_2)) {
flags |= MSG_EOR;
}
return (sendit(sock, &lmsg, &auio, flags));
}
ssize_t
sendmsg(int sock, struct nmsghdr *msg, int flags)
{
struct nmsghdr lmsg;
STRUCT_DECL(nmsghdr, u_lmsg);
struct uio auio;
struct iovec buf[IOV_MAX_STACK], *aiov = buf;
ssize_t iovsize = 0;
int iovcnt;
ssize_t len, rval;
int i;
model_t model;
dprint(1, ("sendmsg(%d, %p, %d)\n", sock, (void *)msg, flags));
model = get_udatamodel();
STRUCT_INIT(u_lmsg, model);
if (flags & MSG_XPG4_2) {
if (copyin(msg, (char *)STRUCT_BUF(u_lmsg),
STRUCT_SIZE(u_lmsg)))
return (set_errno(EFAULT));
} else {
if (copyin(msg, (char *)STRUCT_BUF(u_lmsg),
SIZEOF_STRUCT(omsghdr, model)))
return (set_errno(EFAULT));
flags |= MSG_EOR;
}
lmsg.msg_name = STRUCT_FGETP(u_lmsg, msg_name);
lmsg.msg_namelen = STRUCT_FGET(u_lmsg, msg_namelen);
lmsg.msg_iov = STRUCT_FGETP(u_lmsg, msg_iov);
lmsg.msg_iovlen = STRUCT_FGET(u_lmsg, msg_iovlen);
lmsg.msg_control = STRUCT_FGETP(u_lmsg, msg_control);
lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
iovcnt = lmsg.msg_iovlen;
if (iovcnt <= 0 || iovcnt > IOV_MAX) {
if (iovcnt != 0 || (flags & MSG_XPG4_2))
return (set_errno(EMSGSIZE));
}
if (iovcnt > IOV_MAX_STACK) {
iovsize = iovcnt * sizeof (struct iovec);
aiov = kmem_alloc(iovsize, KM_SLEEP);
}
#ifdef _SYSCALL32_IMPL
if (model == DATAMODEL_ILP32) {
struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
ssize_t iov32size;
ssize32_t count32;
iov32size = iovcnt * sizeof (struct iovec32);
if (iovsize != 0)
aiov32 = kmem_alloc(iov32size, KM_SLEEP);
if (iovcnt != 0 &&
copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
if (iovsize != 0) {
kmem_free(aiov32, iov32size);
kmem_free(aiov, iovsize);
}
return (set_errno(EFAULT));
}
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen32;
iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
if (iovlen32 < 0 || count32 < 0) {
if (iovsize != 0) {
kmem_free(aiov32, iov32size);
kmem_free(aiov, iovsize);
}
return (set_errno(EINVAL));
}
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
if (iovsize != 0)
kmem_free(aiov32, iov32size);
} else
#endif
if (iovcnt != 0 &&
copyin(lmsg.msg_iov, aiov,
(unsigned)iovcnt * sizeof (struct iovec))) {
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (set_errno(EFAULT));
}
len = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
len += iovlen;
if (iovlen < 0 || len < 0) {
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (set_errno(EINVAL));
}
}
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
rval = sendit(sock, &lmsg, &auio, flags);
if (iovsize != 0)
kmem_free(aiov, iovsize);
return (rval);
}
ssize_t
sendto(int sock, void *buffer, size_t len, int flags,
struct sockaddr *name, socklen_t namelen)
{
struct nmsghdr lmsg;
struct uio auio;
struct iovec aiov[1];
dprint(1, ("sendto(%d, %p, %ld, %d, %p, %d)\n",
sock, buffer, len, flags, (void *)name, namelen));
if ((ssize_t)len < 0) {
return (set_errno(EINVAL));
}
aiov[0].iov_base = buffer;
aiov[0].iov_len = len;
auio.uio_loffset = 0;
auio.uio_iov = aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = len;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
lmsg.msg_name = (char *)name;
lmsg.msg_namelen = namelen;
lmsg.msg_control = NULL;
if (!(flags & MSG_XPG4_2)) {
flags |= MSG_EOR;
}
return (sendit(sock, &lmsg, &auio, flags));
}
int
getpeername(int sock, struct sockaddr *name, socklen_t *namelenp, int version)
{
struct sonode *so;
int error;
socklen_t namelen;
socklen_t sock_addrlen;
struct sockaddr *sock_addrp;
dprint(1, ("getpeername(%d, %p, %p)\n",
sock, (void *)name, (void *)namelenp));
if ((so = getsonode(sock, &error, NULL)) == NULL)
goto bad;
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(namelenp, &namelen, sizeof (namelen)) ||
(name == NULL && namelen != 0)) {
error = EFAULT;
goto rel_out;
}
sock_addrlen = so->so_max_addr_len;
sock_addrp = (struct sockaddr *)kmem_alloc(sock_addrlen, KM_SLEEP);
if ((error = socket_getpeername(so, sock_addrp, &sock_addrlen,
B_FALSE, CRED())) == 0) {
ASSERT(sock_addrlen <= so->so_max_addr_len);
error = copyout_name(name, namelen, namelenp,
(void *)sock_addrp, sock_addrlen);
}
kmem_free(sock_addrp, so->so_max_addr_len);
rel_out:
releasef(sock);
bad: return (error != 0 ? set_errno(error) : 0);
}
int
getsockname(int sock, struct sockaddr *name, socklen_t *namelenp, int version)
{
struct sonode *so;
int error;
socklen_t namelen, sock_addrlen;
struct sockaddr *sock_addrp;
dprint(1, ("getsockname(%d, %p, %p)\n",
sock, (void *)name, (void *)namelenp));
if ((so = getsonode(sock, &error, NULL)) == NULL)
goto bad;
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(namelenp, &namelen, sizeof (namelen)) ||
(name == NULL && namelen != 0)) {
error = EFAULT;
goto rel_out;
}
sock_addrlen = so->so_max_addr_len;
sock_addrp = (struct sockaddr *)kmem_alloc(sock_addrlen, KM_SLEEP);
if ((error = socket_getsockname(so, sock_addrp, &sock_addrlen,
CRED())) == 0) {
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
ASSERT(sock_addrlen <= so->so_max_addr_len);
error = copyout_name(name, namelen, namelenp,
(void *)sock_addrp, sock_addrlen);
}
kmem_free(sock_addrp, so->so_max_addr_len);
rel_out:
releasef(sock);
bad: return (error != 0 ? set_errno(error) : 0);
}
int
getsockopt(int sock, int level, int option_name, void *option_value,
socklen_t *option_lenp, int version)
{
struct sonode *so;
socklen_t optlen, optlen_res;
void *optval;
int error;
dprint(1, ("getsockopt(%d, %d, %d, %p, %p)\n",
sock, level, option_name, option_value, (void *)option_lenp));
if ((so = getsonode(sock, &error, NULL)) == NULL)
return (set_errno(error));
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(option_lenp, &optlen, sizeof (optlen))) {
releasef(sock);
return (set_errno(EFAULT));
}
if (optlen > SO_MAXARGSIZE) {
error = EINVAL;
releasef(sock);
return (set_errno(error));
}
optval = kmem_alloc(optlen, KM_SLEEP);
optlen_res = optlen;
error = socket_getsockopt(so, level, option_name, optval,
&optlen_res, (version != SOV_XPG4_2) ? 0 : _SOGETSOCKOPT_XPG4_2,
CRED());
releasef(sock);
if (error) {
kmem_free(optval, optlen);
return (set_errno(error));
}
error = copyout_arg(option_value, optlen, option_lenp,
optval, optlen_res);
kmem_free(optval, optlen);
if (error)
return (set_errno(error));
return (0);
}
int
setsockopt(int sock, int level, int option_name, void *option_value,
socklen_t option_len, int version)
{
struct sonode *so;
intptr_t buffer[2];
void *optval = NULL;
int error;
dprint(1, ("setsockopt(%d, %d, %d, %p, %d)\n",
sock, level, option_name, option_value, option_len));
if ((so = getsonode(sock, &error, NULL)) == NULL)
return (set_errno(error));
if (option_value != NULL) {
if (option_len != 0) {
if (option_len > SO_MAXARGSIZE) {
error = EINVAL;
goto done2;
}
optval = option_len <= sizeof (buffer) ?
&buffer : kmem_alloc((size_t)option_len, KM_SLEEP);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (copyin(option_value, optval, (size_t)option_len)) {
error = EFAULT;
goto done1;
}
}
} else
option_len = 0;
error = socket_setsockopt(so, level, option_name, optval,
(t_uscalar_t)option_len, CRED());
done1:
if (optval != buffer)
kmem_free(optval, (size_t)option_len);
done2:
releasef(sock);
if (error)
return (set_errno(error));
return (0);
}
static int
sockconf_add_sock(int family, int type, int protocol, char *name)
{
int error = 0;
char *kdevpath = NULL;
char *kmodule = NULL;
char *buf = NULL;
size_t pathlen = 0;
struct sockparams *sp;
if (name == NULL)
return (EINVAL);
buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
if ((error = copyinstr(name, buf, MAXPATHLEN, &pathlen)) != 0) {
kmem_free(buf, MAXPATHLEN);
return (error);
}
if (strncmp(buf, "/dev", strlen("/dev")) == 0) {
kdevpath = kmem_alloc(pathlen, KM_SLEEP);
bcopy(buf, kdevpath, pathlen);
kdevpath[pathlen - 1] = '\0';
} else {
kmodule = kmem_alloc(pathlen, KM_SLEEP);
bcopy(buf, kmodule, pathlen);
kmodule[pathlen - 1] = '\0';
pathlen = 0;
}
kmem_free(buf, MAXPATHLEN);
sp = sockparams_create(family, type, protocol, kmodule,
kdevpath, pathlen, 0, KM_SLEEP, &error);
if (sp != NULL) {
error = sockparams_add(sp);
if (error != 0)
sockparams_destroy(sp);
}
return (error);
}
static int
sockconf_remove_sock(int family, int type, int protocol)
{
return (sockparams_delete(family, type, protocol));
}
static int
sockconfig_remove_filter(const char *uname)
{
char kname[SOF_MAXNAMELEN];
size_t len;
int error;
sof_entry_t *ent;
if ((error = copyinstr(uname, kname, SOF_MAXNAMELEN, &len)) != 0)
return (error);
ent = sof_entry_remove_by_name(kname);
if (ent == NULL)
return (ENXIO);
mutex_enter(&ent->sofe_lock);
ASSERT(!(ent->sofe_flags & SOFEF_CONDEMED));
if (ent->sofe_refcnt == 0) {
mutex_exit(&ent->sofe_lock);
sof_entry_free(ent);
} else {
ent->sofe_flags |= SOFEF_CONDEMED;
mutex_exit(&ent->sofe_lock);
}
return (0);
}
static int
sockconfig_add_filter(const char *uname, void *ufilpropp)
{
struct sockconfig_filter_props filprop;
sof_entry_t *ent;
int error;
size_t tuplesz, len;
char hintbuf[SOF_MAXNAMELEN];
ent = kmem_zalloc(sizeof (sof_entry_t), KM_SLEEP);
mutex_init(&ent->sofe_lock, NULL, MUTEX_DEFAULT, NULL);
if ((error = copyinstr(uname, ent->sofe_name, SOF_MAXNAMELEN,
&len)) != 0) {
sof_entry_free(ent);
return (error);
}
if (get_udatamodel() == DATAMODEL_NATIVE) {
if (copyin(ufilpropp, &filprop, sizeof (filprop)) != 0) {
sof_entry_free(ent);
return (EFAULT);
}
}
#ifdef _SYSCALL32_IMPL
else {
struct sockconfig_filter_props32 filprop32;
if (copyin(ufilpropp, &filprop32, sizeof (filprop32)) != 0) {
sof_entry_free(ent);
return (EFAULT);
}
filprop.sfp_modname = (char *)(uintptr_t)filprop32.sfp_modname;
filprop.sfp_autoattach = filprop32.sfp_autoattach;
filprop.sfp_hint = filprop32.sfp_hint;
filprop.sfp_hintarg = (char *)(uintptr_t)filprop32.sfp_hintarg;
filprop.sfp_socktuple_cnt = filprop32.sfp_socktuple_cnt;
filprop.sfp_socktuple =
(sof_socktuple_t *)(uintptr_t)filprop32.sfp_socktuple;
}
#endif
if ((error = copyinstr(filprop.sfp_modname, ent->sofe_modname,
sizeof (ent->sofe_modname), &len)) != 0) {
sof_entry_free(ent);
return (error);
}
if (filprop.sfp_socktuple_cnt == 0 ||
filprop.sfp_socktuple_cnt > SOF_MAXSOCKTUPLECNT) {
sof_entry_free(ent);
return (EINVAL);
}
ent->sofe_flags = filprop.sfp_autoattach ? SOFEF_AUTO : SOFEF_PROG;
ent->sofe_hint = filprop.sfp_hint;
switch (ent->sofe_hint) {
case SOF_HINT_BEFORE:
case SOF_HINT_AFTER:
if ((error = copyinstr(filprop.sfp_hintarg, hintbuf,
sizeof (hintbuf), &len)) != 0) {
sof_entry_free(ent);
return (error);
}
ent->sofe_hintarg = kmem_alloc(len, KM_SLEEP);
bcopy(hintbuf, ent->sofe_hintarg, len);
case SOF_HINT_TOP:
case SOF_HINT_BOTTOM:
if (ent->sofe_flags & SOFEF_PROG) {
sof_entry_free(ent);
return (EINVAL);
}
break;
case SOF_HINT_NONE:
break;
default:
sof_entry_free(ent);
return (EINVAL);
}
ent->sofe_socktuple_cnt = filprop.sfp_socktuple_cnt;
tuplesz = sizeof (sof_socktuple_t) * ent->sofe_socktuple_cnt;
ent->sofe_socktuple = kmem_alloc(tuplesz, KM_SLEEP);
if (get_udatamodel() == DATAMODEL_NATIVE) {
if (copyin(filprop.sfp_socktuple, ent->sofe_socktuple,
tuplesz)) {
sof_entry_free(ent);
return (EFAULT);
}
}
#ifdef _SYSCALL32_IMPL
else {
int i;
caddr_t data = (caddr_t)filprop.sfp_socktuple;
sof_socktuple_t *tup = ent->sofe_socktuple;
sof_socktuple32_t tup32;
tup = ent->sofe_socktuple;
for (i = 0; i < ent->sofe_socktuple_cnt; i++, tup++) {
ASSERT(tup < ent->sofe_socktuple + tuplesz);
if (copyin(data, &tup32, sizeof (tup32)) != 0) {
sof_entry_free(ent);
return (EFAULT);
}
tup->sofst_family = tup32.sofst_family;
tup->sofst_type = tup32.sofst_type;
tup->sofst_protocol = tup32.sofst_protocol;
data += sizeof (tup32);
}
}
#endif
if ((error = sof_entry_add(ent)) != 0)
sof_entry_free(ent);
return (error);
}
int
sockconfig(int cmd, void *arg1, void *arg2, void *arg3, void *arg4)
{
int error = 0;
if (secpolicy_net_config(CRED(), B_FALSE) != 0)
return (set_errno(EPERM));
switch (cmd) {
case SOCKCONFIG_ADD_SOCK:
error = sockconf_add_sock((int)(uintptr_t)arg1,
(int)(uintptr_t)arg2, (int)(uintptr_t)arg3, arg4);
break;
case SOCKCONFIG_REMOVE_SOCK:
error = sockconf_remove_sock((int)(uintptr_t)arg1,
(int)(uintptr_t)arg2, (int)(uintptr_t)arg3);
break;
case SOCKCONFIG_ADD_FILTER:
error = sockconfig_add_filter((const char *)arg1, arg2);
break;
case SOCKCONFIG_REMOVE_FILTER:
error = sockconfig_remove_filter((const char *)arg1);
break;
case SOCKCONFIG_GET_SOCKTABLE:
error = sockparams_copyout_socktable((int)(uintptr_t)arg1);
break;
default:
#ifdef DEBUG
cmn_err(CE_NOTE, "sockconfig: unkonwn subcommand %d", cmd);
#endif
error = EINVAL;
break;
}
if (error != 0) {
eprintline(error);
return (set_errno(error));
}
return (0);
}
uint_t sendfile_read_size = 1024 * 1024;
#define SENDFILE_REQ_LOWAT 3 * 1024 * 1024
uint_t sendfile_req_lowat = SENDFILE_REQ_LOWAT;
uint_t sendfile_req_hiwat = 10 * SENDFILE_REQ_LOWAT;
struct sendfile_stats sf_stats;
struct sendfile_queue *snfq;
clock_t snfq_timeout;
off64_t sendfile_max_size;
static void snf_enque(snf_req_t *, mblk_t *);
static mblk_t *snf_deque(snf_req_t *);
void
sendfile_init(void)
{
snfq = kmem_zalloc(sizeof (struct sendfile_queue), KM_SLEEP);
mutex_init(&snfq->snfq_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&snfq->snfq_cv, NULL, CV_DEFAULT, NULL);
snfq->snfq_max_threads = max_ncpus;
snfq_timeout = SNFQ_TIMEOUT;
sendfile_max_size = MAXOFFSET_T;
}
static void
snf_enque(snf_req_t *sr, mblk_t *mp)
{
mp->b_next = NULL;
mutex_enter(&sr->sr_lock);
if (sr->sr_mp_head == NULL) {
sr->sr_mp_head = sr->sr_mp_tail = mp;
cv_signal(&sr->sr_cv);
} else {
sr->sr_mp_tail->b_next = mp;
sr->sr_mp_tail = mp;
}
sr->sr_qlen += MBLKL(mp);
while ((sr->sr_qlen > sr->sr_hiwat) &&
(sr->sr_write_error == 0)) {
sf_stats.ss_full_waits++;
cv_wait(&sr->sr_cv, &sr->sr_lock);
}
mutex_exit(&sr->sr_lock);
}
static mblk_t *
snf_deque(snf_req_t *sr)
{
mblk_t *mp;
mutex_enter(&sr->sr_lock);
if (((sr->sr_read_error & ~SR_READ_DONE) != 0) ||
((sr->sr_read_error & SR_READ_DONE) &&
sr->sr_mp_head == NULL)) {
mutex_exit(&sr->sr_lock);
return (NULL);
}
while ((sr->sr_read_error == 0) && (sr->sr_mp_head == NULL)) {
sf_stats.ss_empty_waits++;
cv_wait(&sr->sr_cv, &sr->sr_lock);
}
if (((sr->sr_read_error & ~SR_READ_DONE) == 0) &&
(sr->sr_mp_head != NULL)) {
mp = sr->sr_mp_head;
sr->sr_mp_head = mp->b_next;
sr->sr_qlen -= MBLKL(mp);
if (sr->sr_qlen < sr->sr_lowat)
cv_signal(&sr->sr_cv);
mutex_exit(&sr->sr_lock);
mp->b_next = NULL;
return (mp);
}
mutex_exit(&sr->sr_lock);
return (NULL);
}
void
snf_async_read(snf_req_t *sr)
{
size_t iosize;
u_offset_t fileoff;
u_offset_t size;
int ret_size;
int error;
file_t *fp;
mblk_t *mp;
struct vnode *vp;
int extra = 0;
int maxblk = 0;
int wroff = 0;
struct sonode *so = NULL;
fp = sr->sr_fp;
size = sr->sr_file_size;
fileoff = sr->sr_file_off;
(void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_ON, 0,
kcred, NULL, NULL);
vp = sr->sr_vp;
if (vp->v_type == VSOCK) {
stdata_t *stp;
so = VTOSO(vp);
stp = vp->v_stream;
if (stp == NULL) {
wroff = so->so_proto_props.sopp_wroff;
maxblk = so->so_proto_props.sopp_maxblk;
extra = wroff + so->so_proto_props.sopp_tail;
} else {
wroff = (int)(stp->sd_wroff);
maxblk = (int)(stp->sd_maxblk);
extra = wroff + (int)(stp->sd_tail);
}
}
while ((size != 0) && (sr->sr_write_error == 0)) {
iosize = (int)MIN(sr->sr_maxpsz, size);
if (vp->v_type == VSOCK &&
so->so_filter_active > 0 && maxblk != INFPSZ)
iosize = (int)MIN(iosize, maxblk);
if (is_system_labeled()) {
mp = allocb_cred(iosize + extra, CRED(),
curproc->p_pid);
} else {
mp = allocb(iosize + extra, BPRI_MED);
}
if (mp == NULL) {
error = EAGAIN;
break;
}
mp->b_rptr += wroff;
ret_size = soreadfile(fp, mp->b_rptr, fileoff, &error, iosize);
if ((error != 0) || (ret_size == 0)) {
freeb(mp);
break;
}
mp->b_wptr = mp->b_rptr + ret_size;
snf_enque(sr, mp);
size -= ret_size;
fileoff += ret_size;
}
(void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_OFF, 0,
kcred, NULL, NULL);
mutex_enter(&sr->sr_lock);
sr->sr_read_error = error;
sr->sr_read_error |= SR_READ_DONE;
cv_signal(&sr->sr_cv);
mutex_exit(&sr->sr_lock);
}
void
snf_async_thread(void)
{
snf_req_t *sr;
callb_cpr_t cprinfo;
clock_t time_left = 1;
CALLB_CPR_INIT(&cprinfo, &snfq->snfq_lock, callb_generic_cpr, "snfq");
mutex_enter(&snfq->snfq_lock);
for (;;) {
while ((sr = snfq->snfq_req_head) == NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
if (time_left <= 0) {
snfq->snfq_svc_threads--;
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
snfq->snfq_idle_cnt++;
time_left = cv_reltimedwait(&snfq->snfq_cv,
&snfq->snfq_lock, snfq_timeout, TR_CLOCK_TICK);
snfq->snfq_idle_cnt--;
CALLB_CPR_SAFE_END(&cprinfo, &snfq->snfq_lock);
}
snfq->snfq_req_head = sr->sr_next;
snfq->snfq_req_cnt--;
mutex_exit(&snfq->snfq_lock);
snf_async_read(sr);
mutex_enter(&snfq->snfq_lock);
}
}
snf_req_t *
create_thread(int operation, struct vnode *vp, file_t *fp,
u_offset_t fileoff, u_offset_t size)
{
snf_req_t *sr;
stdata_t *stp;
sr = (snf_req_t *)kmem_zalloc(sizeof (snf_req_t), KM_SLEEP);
sr->sr_vp = vp;
sr->sr_fp = fp;
stp = vp->v_stream;
if (stp != NULL && stp->sd_qn_maxpsz > 0) {
sr->sr_maxpsz = MIN(MAXBSIZE, stp->sd_qn_maxpsz);
} else {
sr->sr_maxpsz = MAXBSIZE;
}
sr->sr_operation = operation;
sr->sr_file_off = fileoff;
sr->sr_file_size = size;
sr->sr_hiwat = sendfile_req_hiwat;
sr->sr_lowat = sendfile_req_lowat;
mutex_init(&sr->sr_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&sr->sr_cv, NULL, CV_DEFAULT, NULL);
mutex_enter(&snfq->snfq_lock);
if (snfq->snfq_req_cnt >= snfq->snfq_idle_cnt &&
snfq->snfq_svc_threads < snfq->snfq_max_threads) {
(void) thread_create(NULL, 0, &snf_async_thread, 0, 0, &p0,
TS_RUN, minclsyspri);
snfq->snfq_svc_threads++;
}
if (snfq->snfq_req_head == NULL) {
snfq->snfq_req_head = snfq->snfq_req_tail = sr;
cv_signal(&snfq->snfq_cv);
} else {
snfq->snfq_req_tail->sr_next = sr;
snfq->snfq_req_tail = sr;
}
snfq->snfq_req_cnt++;
mutex_exit(&snfq->snfq_lock);
return (sr);
}
int
snf_direct_io(file_t *fp, file_t *rfp, u_offset_t fileoff, u_offset_t size,
ssize_t *count)
{
snf_req_t *sr;
mblk_t *mp;
int iosize;
int error = 0;
short fflag;
struct vnode *vp;
int ksize;
struct nmsghdr msg;
ksize = 0;
*count = 0;
bzero(&msg, sizeof (msg));
vp = fp->f_vnode;
fflag = fp->f_flag;
if ((sr = create_thread(READ_OP, vp, rfp, fileoff, size)) == NULL)
return (EAGAIN);
while ((mp = snf_deque(sr)) != NULL) {
if (ISSIG(curthread, JUSTLOOKING)) {
freeb(mp);
error = EINTR;
break;
}
iosize = MBLKL(mp);
error = socket_sendmblk(VTOSO(vp), &msg, fflag, CRED(), &mp);
if (error != 0) {
if (mp != NULL)
freeb(mp);
break;
}
ksize += iosize;
}
*count = ksize;
mutex_enter(&sr->sr_lock);
sr->sr_write_error = error;
cv_signal(&sr->sr_cv);
while (!(sr->sr_read_error & SR_READ_DONE)) {
cv_wait(&sr->sr_cv, &sr->sr_lock);
}
if (error == 0)
error = (sr->sr_read_error & ~SR_READ_DONE);
if (error != 0) {
mblk_t *next_mp;
mp = sr->sr_mp_head;
while (mp != NULL) {
next_mp = mp->b_next;
mp->b_next = NULL;
freeb(mp);
mp = next_mp;
}
}
mutex_exit(&sr->sr_lock);
kmem_free(sr, sizeof (snf_req_t));
return (error);
}
#define SNF_VPMMAXPGS (VPMMAXPGS/2)
#define SNF_MAXVMAPS (SNF_VPMMAXPGS + 1)
typedef struct {
unsigned int snfv_ref;
frtn_t snfv_frtn;
vnode_t *snfv_vp;
struct vmap snfv_vml[SNF_MAXVMAPS];
} snf_vmap_desbinfo;
typedef struct {
frtn_t snfi_frtn;
caddr_t snfi_base;
uint_t snfi_mapoff;
size_t snfi_len;
vnode_t *snfi_vp;
} snf_smap_desbinfo;
void
snf_vmap_desbfree(snf_vmap_desbinfo *snfv)
{
ASSERT(snfv->snfv_ref != 0);
if (atomic_dec_32_nv(&snfv->snfv_ref) == 0) {
vpm_unmap_pages(snfv->snfv_vml, S_READ);
VN_RELE(snfv->snfv_vp);
kmem_free(snfv, sizeof (snf_vmap_desbinfo));
}
}
void
snf_smap_desbfree(snf_smap_desbinfo *snfi)
{
if (! IS_KPM_ADDR(snfi->snfi_base)) {
(void) segmap_fault(kas.a_hat, segkmap,
(caddr_t)(uintptr_t)(((uintptr_t)snfi->snfi_base +
snfi->snfi_mapoff) & PAGEMASK), snfi->snfi_len,
F_SOFTUNLOCK, S_OTHER);
}
(void) segmap_release(segkmap, snfi->snfi_base, SM_DONTNEED);
VN_RELE(snfi->snfi_vp);
kmem_free(snfi, sizeof (*snfi));
}
int
snf_segmap(file_t *fp, vnode_t *fvp, u_offset_t fileoff, u_offset_t total_size,
ssize_t *count, boolean_t nowait)
{
caddr_t base;
int mapoff;
vnode_t *vp;
mblk_t *mp = NULL;
int chain_size;
int error;
clock_t deadlk_wait;
short fflag;
int ksize;
struct vattr va;
boolean_t dowait = B_FALSE;
struct nmsghdr msg;
vp = fp->f_vnode;
fflag = fp->f_flag;
ksize = 0;
bzero(&msg, sizeof (msg));
for (;;) {
if (ISSIG(curthread, JUSTLOOKING)) {
error = EINTR;
break;
}
if (vpm_enable) {
snf_vmap_desbinfo *snfv;
mblk_t *nmp;
int mblk_size;
int maxsize;
int i;
mapoff = fileoff & PAGEOFFSET;
maxsize = MIN((SNF_VPMMAXPGS * PAGESIZE), total_size);
snfv = kmem_zalloc(sizeof (snf_vmap_desbinfo),
KM_SLEEP);
deadlk_wait = 0;
while ((error = vpm_map_pages(fvp, fileoff,
(size_t)maxsize, (VPM_FETCHPAGE), snfv->snfv_vml,
SNF_MAXVMAPS, NULL, S_READ)) == EDEADLK) {
deadlk_wait += (deadlk_wait < 5) ? 1 : 4;
if ((error = delay_sig(deadlk_wait)) != 0) {
break;
}
}
if (error != 0) {
kmem_free(snfv, sizeof (snf_vmap_desbinfo));
error = (error == EINTR) ? EINTR : EIO;
goto out;
}
snfv->snfv_frtn.free_func = snf_vmap_desbfree;
snfv->snfv_frtn.free_arg = (caddr_t)snfv;
chain_size = 0;
for (i = 0; (snfv->snfv_vml[i].vs_addr != NULL) &&
total_size > 0; i++) {
ASSERT(chain_size < maxsize);
mblk_size = MIN(snfv->snfv_vml[i].vs_len -
mapoff, total_size);
nmp = esballoca(
(uchar_t *)snfv->snfv_vml[i].vs_addr +
mapoff, mblk_size, BPRI_HI,
&snfv->snfv_frtn);
if (nmp == NULL) {
if (i == 0) {
vpm_unmap_pages(snfv->snfv_vml,
S_READ);
kmem_free(snfv,
sizeof (snf_vmap_desbinfo));
error = EAGAIN;
goto out;
}
break;
}
nmp->b_datap->db_struioflag |= STRUIO_ZC;
nmp->b_wptr += mblk_size;
chain_size += mblk_size;
fileoff += mblk_size;
total_size -= mblk_size;
snfv->snfv_ref++;
mapoff = 0;
if (i > 0)
linkb(mp, nmp);
else
mp = nmp;
}
VN_HOLD(fvp);
snfv->snfv_vp = fvp;
} else {
snf_smap_desbinfo *snfi;
mapoff = fileoff & MAXBOFFSET;
chain_size = MAXBSIZE - mapoff;
if (chain_size > total_size)
chain_size = total_size;
base = segmap_getmapflt(segkmap, fvp, fileoff,
chain_size, segmap_kpm ? SM_FAULT : 0, S_READ);
snfi = kmem_alloc(sizeof (*snfi), KM_SLEEP);
snfi->snfi_len = (size_t)roundup(mapoff+chain_size,
PAGESIZE)- (mapoff & PAGEMASK);
deadlk_wait = 0;
while ((error = FC_ERRNO(segmap_fault(kas.a_hat,
segkmap, (caddr_t)(uintptr_t)(((uintptr_t)base +
mapoff) & PAGEMASK), snfi->snfi_len, F_SOFTLOCK,
S_READ))) == EDEADLK) {
deadlk_wait += (deadlk_wait < 5) ? 1 : 4;
if ((error = delay_sig(deadlk_wait)) != 0) {
break;
}
}
if (error != 0) {
(void) segmap_release(segkmap, base, 0);
kmem_free(snfi, sizeof (*snfi));
error = (error == EINTR) ? EINTR : EIO;
goto out;
}
snfi->snfi_frtn.free_func = snf_smap_desbfree;
snfi->snfi_frtn.free_arg = (caddr_t)snfi;
snfi->snfi_base = base;
snfi->snfi_mapoff = mapoff;
mp = esballoca((uchar_t *)base + mapoff, chain_size,
BPRI_HI, &snfi->snfi_frtn);
if (mp == NULL) {
(void) segmap_fault(kas.a_hat, segkmap,
(caddr_t)(uintptr_t)(((uintptr_t)base +
mapoff) & PAGEMASK), snfi->snfi_len,
F_SOFTUNLOCK, S_OTHER);
(void) segmap_release(segkmap, base, 0);
kmem_free(snfi, sizeof (*snfi));
freemsg(mp);
error = EAGAIN;
goto out;
}
VN_HOLD(fvp);
snfi->snfi_vp = fvp;
mp->b_wptr += chain_size;
mp->b_datap->db_struioflag |= STRUIO_ZC;
fileoff += chain_size;
total_size -= chain_size;
}
if (total_size == 0 && !nowait) {
ASSERT(!dowait);
dowait = B_TRUE;
mp->b_datap->db_struioflag |= STRUIO_ZCNOTIFY;
}
VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL);
error = socket_sendmblk(VTOSO(vp), &msg, fflag, CRED(), &mp);
if (error != 0) {
*count = ksize + (chain_size - msgdsize(mp));
if (mp != NULL)
freemsg(mp);
return (error);
}
ksize += chain_size;
if (total_size == 0)
goto done;
(void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL);
va.va_mask = AT_SIZE;
error = VOP_GETATTR(fvp, &va, 0, kcred, NULL);
if (error)
break;
if (fileoff >= va.va_size)
break;
if (total_size + fileoff > va.va_size)
total_size = va.va_size - fileoff;
}
out:
VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL);
done:
*count = ksize;
if (dowait) {
stdata_t *stp;
stp = vp->v_stream;
if (stp == NULL) {
struct sonode *so;
so = VTOSO(vp);
error = so_zcopy_wait(so);
} else {
mutex_enter(&stp->sd_lock);
while (!(stp->sd_flag & STZCNOTIFY)) {
if (cv_wait_sig(&stp->sd_zcopy_wait,
&stp->sd_lock) == 0) {
error = EINTR;
break;
}
}
stp->sd_flag &= ~STZCNOTIFY;
mutex_exit(&stp->sd_lock);
}
}
return (error);
}
int
snf_cache(file_t *fp, vnode_t *fvp, u_offset_t fileoff, u_offset_t size,
uint_t maxpsz, ssize_t *count)
{
struct vnode *vp;
mblk_t *mp;
int iosize;
int extra = 0;
int error;
short fflag;
int ksize;
int ioflag;
struct uio auio;
struct iovec aiov;
struct vattr va;
int maxblk = 0;
int wroff = 0;
struct sonode *so = NULL;
struct nmsghdr msg;
vp = fp->f_vnode;
if (vp->v_type == VSOCK) {
stdata_t *stp;
so = VTOSO(vp);
stp = vp->v_stream;
if (stp == NULL) {
wroff = so->so_proto_props.sopp_wroff;
maxblk = so->so_proto_props.sopp_maxblk;
extra = wroff + so->so_proto_props.sopp_tail;
} else {
wroff = (int)(stp->sd_wroff);
maxblk = (int)(stp->sd_maxblk);
extra = wroff + (int)(stp->sd_tail);
}
}
bzero(&msg, sizeof (msg));
fflag = fp->f_flag;
ksize = 0;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_llimit = MAXOFFSET_T;
auio.uio_fmode = fflag;
auio.uio_extflg = UIO_COPY_CACHED;
ioflag = auio.uio_fmode & (FSYNC|FDSYNC|FRSYNC);
if ((ioflag & FRSYNC) == 0)
ioflag &= ~(FSYNC|FDSYNC);
for (;;) {
if (ISSIG(curthread, JUSTLOOKING)) {
error = EINTR;
break;
}
iosize = (int)MIN(maxpsz, size);
if (vp->v_type == VSOCK &&
so->so_filter_active > 0 && maxblk != INFPSZ)
iosize = (int)MIN(iosize, maxblk);
if (is_system_labeled()) {
mp = allocb_cred(iosize + extra, CRED(),
curproc->p_pid);
} else {
mp = allocb(iosize + extra, BPRI_MED);
}
if (mp == NULL) {
error = EAGAIN;
break;
}
mp->b_rptr += wroff;
aiov.iov_base = (caddr_t)mp->b_rptr;
aiov.iov_len = iosize;
auio.uio_loffset = fileoff;
auio.uio_resid = iosize;
error = VOP_READ(fvp, &auio, ioflag, fp->f_cred, NULL);
iosize -= auio.uio_resid;
if (error == EINTR && iosize != 0)
error = 0;
if (error != 0 || iosize == 0) {
freeb(mp);
break;
}
mp->b_wptr = mp->b_rptr + iosize;
VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL);
error = socket_sendmblk(VTOSO(vp), &msg, fflag, CRED(), &mp);
if (error != 0) {
*count = ksize;
if (mp != NULL)
freeb(mp);
return (error);
}
ksize += iosize;
size -= iosize;
if (size == 0)
goto done;
fileoff += iosize;
(void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL);
va.va_mask = AT_SIZE;
error = VOP_GETATTR(fvp, &va, 0, kcred, NULL);
if (error)
break;
if (fileoff >= va.va_size)
size = 0;
else if (size + fileoff > va.va_size)
size = va.va_size - fileoff;
}
VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL);
done:
*count = ksize;
return (error);
}
#if defined(_SYSCALL32_IMPL) || defined(_ILP32)
int
sosendfile64(file_t *fp, file_t *rfp, const struct ksendfilevec64 *sfv,
ssize32_t *count32)
{
ssize32_t sfv_len;
u_offset_t sfv_off, va_size;
struct vnode *vp, *fvp, *realvp;
struct vattr va;
stdata_t *stp;
ssize_t count = 0;
int error = 0;
boolean_t dozcopy = B_FALSE;
uint_t maxpsz;
sfv_len = (ssize32_t)sfv->sfv_len;
if (sfv_len < 0) {
error = EINVAL;
goto out;
}
if (sfv_len == 0) goto out;
sfv_off = (u_offset_t)sfv->sfv_off;
if (sfv_off > MAXOFFSET_T) {
error = EINVAL;
goto out;
}
if (sfv_off + sfv_len > MAXOFFSET_T)
sfv_len = (ssize32_t)(MAXOFFSET_T - sfv_off);
if (sfv_len > sendfile_max_size) {
sf_stats.ss_file_not_cached++;
error = snf_direct_io(fp, rfp, sfv_off, (u_offset_t)sfv_len,
&count);
goto out;
}
fvp = rfp->f_vnode;
if (VOP_REALVP(fvp, &realvp, NULL) == 0)
fvp = realvp;
(void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL);
va.va_mask = AT_SIZE;
error = VOP_GETATTR(fvp, &va, 0, kcred, NULL);
va_size = va.va_size;
if ((error != 0) || (va_size == 0) || (sfv_off >= va_size)) {
VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL);
goto out;
}
if (sfv_off + sfv_len > va_size)
sfv_len = va_size - sfv_off;
vp = fp->f_vnode;
stp = vp->v_stream;
if (sfv_len >= MAXBSIZE && (sfv_len >= (va_size >> 1) ||
(sfv->sfv_flag & SFV_NOWAIT) || sfv_len >= 0x1000000) &&
!vn_has_flocks(fvp) && !(fvp->v_flag & VNOMAP)) {
uint_t copyflag;
copyflag = stp != NULL ? stp->sd_copyflag :
VTOSO(vp)->so_proto_props.sopp_zcopyflag;
if ((copyflag & (STZCVMSAFE|STZCVMUNSAFE)) == 0) {
int on = 1;
if (socket_setsockopt(VTOSO(vp), SOL_SOCKET,
SO_SND_COPYAVOID, &on, sizeof (on), CRED()) == 0)
dozcopy = B_TRUE;
} else {
dozcopy = copyflag & STZCVMSAFE;
}
}
if (dozcopy) {
sf_stats.ss_file_segmap++;
error = snf_segmap(fp, fvp, sfv_off, (u_offset_t)sfv_len,
&count, ((sfv->sfv_flag & SFV_NOWAIT) != 0));
} else {
if (vp->v_type == VSOCK && stp == NULL) {
sonode_t *so = VTOSO(vp);
maxpsz = so->so_proto_props.sopp_maxpsz;
} else if (stp != NULL) {
maxpsz = stp->sd_qn_maxpsz;
} else {
maxpsz = maxphys;
}
if (maxpsz == INFPSZ)
maxpsz = maxphys;
else
maxpsz = roundup(maxpsz, MAXBSIZE);
sf_stats.ss_file_cached++;
error = snf_cache(fp, fvp, sfv_off, (u_offset_t)sfv_len,
maxpsz, &count);
}
out:
releasef(sfv->sfv_fd);
*count32 = (ssize32_t)count;
return (error);
}
#endif
#ifdef _SYSCALL32_IMPL
ssize_t
recv32(int32_t sock, caddr32_t buffer, size32_t len, int32_t flags)
{
return (recv(sock, (void *)(uintptr_t)buffer, (ssize32_t)len, flags));
}
ssize_t
recvfrom32(int32_t sock, caddr32_t buffer, size32_t len, int32_t flags,
caddr32_t name, caddr32_t namelenp)
{
return (recvfrom(sock, (void *)(uintptr_t)buffer, (ssize32_t)len, flags,
(void *)(uintptr_t)name, (void *)(uintptr_t)namelenp));
}
ssize_t
send32(int32_t sock, caddr32_t buffer, size32_t len, int32_t flags)
{
return (send(sock, (void *)(uintptr_t)buffer, (ssize32_t)len, flags));
}
ssize_t
sendto32(int32_t sock, caddr32_t buffer, size32_t len, int32_t flags,
caddr32_t name, socklen_t namelen)
{
return (sendto(sock, (void *)(uintptr_t)buffer, (ssize32_t)len, flags,
(void *)(uintptr_t)name, namelen));
}
#endif
int
soaccept(struct sonode *so, int fflag, struct sonode **nsop)
{
return (socket_accept(so, fflag, CRED(), nsop));
}
int
sobind(struct sonode *so, struct sockaddr *name, socklen_t namelen,
int backlog, int flags)
{
int error;
error = socket_bind(so, name, namelen, flags, CRED());
if (error == 0 && backlog != 0)
return (socket_listen(so, backlog, CRED()));
return (error);
}
int
solisten(struct sonode *so, int backlog)
{
return (socket_listen(so, backlog, CRED()));
}
int
soconnect(struct sonode *so, struct sockaddr *name, socklen_t namelen,
int fflag, int flags)
{
return (socket_connect(so, name, namelen, fflag, flags, CRED()));
}
int
sorecvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop)
{
return (socket_recvmsg(so, msg, uiop, CRED()));
}
int
sosendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop)
{
return (socket_sendmsg(so, msg, uiop, CRED()));
}
int
soshutdown(struct sonode *so, int how)
{
return (socket_shutdown(so, how, CRED()));
}
int
sogetsockopt(struct sonode *so, int level, int option_name, void *optval,
socklen_t *optlenp, int flags)
{
return (socket_getsockopt(so, level, option_name, optval, optlenp,
flags, CRED()));
}
int
sosetsockopt(struct sonode *so, int level, int option_name, const void *optval,
t_uscalar_t optlen)
{
return (socket_setsockopt(so, level, option_name, optval, optlen,
CRED()));
}
struct sonode *
socreate(struct sockparams *sp, int family, int type, int protocol, int version,
int *errorp)
{
struct sonode *so;
ASSERT(sp != NULL);
so = sp->sp_smod_info->smod_sock_create_func(sp, family, type, protocol,
version, SOCKET_SLEEP, errorp, CRED());
if (so == NULL) {
SOCKPARAMS_DEC_REF(sp);
} else {
if ((*errorp = SOP_INIT(so, NULL, CRED(), SOCKET_SLEEP)) == 0) {
(void) VOP_OPEN(&SOTOV(so), FREAD|FWRITE, CRED(), NULL);
} else {
socket_destroy(so);
so = NULL;
}
}
return (so);
}