diff options
author | jlemon <jlemon@FreeBSD.org> | 2001-03-01 21:44:40 +0000 |
---|---|---|
committer | jlemon <jlemon@FreeBSD.org> | 2001-03-01 21:44:40 +0000 |
commit | 0bdef2632976d1c6964f12eec3370f9292d21da9 (patch) | |
tree | 82b2074798802c8d00222f17d7be84b156a7959c /sys/compat/linux/linux_socket.c | |
parent | 6e8fd9ef894dc97549b6f1448de127103fbe1c15 (diff) | |
download | FreeBSD-src-0bdef2632976d1c6964f12eec3370f9292d21da9.zip FreeBSD-src-0bdef2632976d1c6964f12eec3370f9292d21da9.tar.gz |
Correctly emulate linux_connect. For nonblocking sockets, the behavior
is to return EINPROGRESS, EALREADY, (so_error ONCE), EISCONN. Certain
linux applications rely on the so_error (normally 0) being returned in
order to operate properly.
Tested by: Thomas Moestl <tmoestl@gmx.net>
Diffstat (limited to 'sys/compat/linux/linux_socket.c')
-rw-r--r-- | sys/compat/linux/linux_socket.c | 72 |
1 files changed, 21 insertions, 51 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 4f35dc4..079d5dd 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -40,7 +40,9 @@ #include <sys/systm.h> #include <sys/sysproto.h> #include <sys/fcntl.h> +#include <sys/file.h> #include <sys/socket.h> +#include <sys/socketvar.h> #include <sys/uio.h> #include <netinet/in.h> @@ -392,6 +394,8 @@ linux_connect(struct proc *p, struct linux_connect_args *args) caddr_t name; int namelen; } */ bsd_args; + struct socket *so; + struct file *fp; int error; #ifdef __alpha__ @@ -405,59 +409,25 @@ linux_connect(struct proc *p, struct linux_connect_args *args) bsd_args.name = (caddr_t)linux_args.name; bsd_args.namelen = linux_args.namelen; error = connect(p, &bsd_args); - if (error == EISCONN) { - /* - * Linux doesn't return EISCONN the first time it occurs, - * when on a non-blocking socket. Instead it returns the - * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD. - */ - struct fcntl_args /* { - int fd; - int cmd; - int arg; - } */ bsd_fcntl_args; - struct getsockopt_args /* { - int s; - int level; - int name; - caddr_t val; - int *avalsize; - } */ bsd_getsockopt_args; - void *status, *statusl; - int stat, statl = sizeof stat; - caddr_t sg; - - /* Check for non-blocking */ - bsd_fcntl_args.fd = linux_args.s; - bsd_fcntl_args.cmd = F_GETFL; - bsd_fcntl_args.arg = 0; - error = fcntl(p, &bsd_fcntl_args); - if (error == 0 && (p->p_retval[0] & O_NONBLOCK)) { - sg = stackgap_init(); - status = stackgap_alloc(&sg, sizeof stat); - statusl = stackgap_alloc(&sg, sizeof statusl); - - if ((error = copyout(&statl, statusl, sizeof statl))) - return (error); - - bsd_getsockopt_args.s = linux_args.s; - bsd_getsockopt_args.level = SOL_SOCKET; - bsd_getsockopt_args.name = SO_ERROR; - bsd_getsockopt_args.val = status; - bsd_getsockopt_args.avalsize = statusl; - - error = getsockopt(p, &bsd_getsockopt_args); - if (error) - return (error); - - if ((error = copyin(status, &stat, sizeof stat))) - return (error); + if (error != EISCONN) + return (error); - p->p_retval[0] = stat; - return (0); - } + /* + * Linux doesn't return EISCONN the first time it occurs, + * when on a non-blocking socket. Instead it returns the + * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD. + */ + error = holdsock(p->p_fd, linux_args.s, &fp); + if (error) + return (error); + error = EISCONN; + if (fp->f_flag & FNONBLOCK) { + so = (struct socket *)fp->f_data; + if ((u_int)so->so_emuldata != 0) + error = so->so_error; + so->so_emuldata = (void *)1; } - + fdrop(fp, p); return (error); } |