From 0bdef2632976d1c6964f12eec3370f9292d21da9 Mon Sep 17 00:00:00 2001 From: jlemon Date: Thu, 1 Mar 2001 21:44:40 +0000 Subject: [PATCH] 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 --- sys/compat/linux/linux_socket.c | 72 ++++++++++----------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 4f35dc43bfac..079d5dd4ae0f 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -40,7 +40,9 @@ #include #include #include +#include #include +#include #include #include @@ -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; + if (error != EISCONN) + return (error); - /* 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); - - 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); }