summaryrefslogtreecommitdiffstats
path: root/sys/compat
diff options
context:
space:
mode:
authorjlemon <jlemon@FreeBSD.org>2001-03-01 21:44:40 +0000
committerjlemon <jlemon@FreeBSD.org>2001-03-01 21:44:40 +0000
commit0bdef2632976d1c6964f12eec3370f9292d21da9 (patch)
tree82b2074798802c8d00222f17d7be84b156a7959c /sys/compat
parent6e8fd9ef894dc97549b6f1448de127103fbe1c15 (diff)
downloadFreeBSD-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')
-rw-r--r--sys/compat/linux/linux_socket.c72
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);
}
OpenPOWER on IntegriCloud