patch-2.2.4 linux/arch/sparc64/kernel/sys_sparc32.c

Next file: linux/arch/sparc64/kernel/systbls.S
Previous file: linux/arch/sparc64/kernel/sys_sparc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.100 1998/11/08 11:14:00 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107 1999/03/05 13:21:02 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -43,6 +43,7 @@
 #include <linux/poll.h>
 #include <linux/personality.h>
 #include <linux/stat.h>
+#include <linux/timex.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -50,6 +51,8 @@
 #include <asm/fpumacro.h>
 #include <asm/semaphore.h>
 
+#include <net/scm.h>
+
 /* Use this to get at 32-bit user passed pointers. */
 /* Things to consider: the low-level assembly stub does
    srl x, 0, x for first four arguments, so if you have
@@ -74,15 +77,6 @@
 	__ret;				\
 })
 
-static inline char * get_page(void)
-{
-	char * res;
-	res = (char *)__get_free_page(GFP_KERNEL);
-	return res;
-}
-
-#define putname32 putname
-
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
  * kernel data space before using them..
@@ -109,13 +103,13 @@
 	char *tmp, *result;
 
 	result = ERR_PTR(-ENOMEM);
-	tmp = get_page();
+	tmp = (char *)__get_free_page(GFP_KERNEL);
 	if (tmp)  {
 		int retval = do_getname32(filename, tmp);
 
 		result = tmp;
 		if (retval < 0) {
-			putname32(tmp);
+			putname(tmp);
 			result = ERR_PTR(retval);
 		}
 	}
@@ -243,7 +237,10 @@
 	err = -EFAULT;
 	if (get_user (pad, (u32 *)uptr))
 		goto out;
-	fourth.__pad = (void *)A(pad);
+	if(third == SETVAL)
+		fourth.val = (int)pad;
+	else
+		fourth.__pad = (void *)A(pad);
 	if (IPCOP_MASK (third) &
 	    (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) |
 	     IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) |
@@ -652,7 +649,7 @@
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
-	putname32 (spec);
+	putname (spec);
 	if (cmds == Q_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
 		((struct dqblk32 *)&d)->dqb_itime = i;
@@ -696,7 +693,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_statfs((const char *)pth, &s);
 		set_fs (old_fs);
-		putname32 (pth);
+		putname (pth);
 		if (put_statfs(buf, &s))
 			return -EFAULT;
 	}
@@ -744,7 +741,7 @@
 		set_fs (KERNEL_DS); 
 		ret = sys_utime(filenam, &t);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 	}
 	return ret;
 }
@@ -796,8 +793,9 @@
 	}
 
 	inode = file->f_dentry->d_inode;
-	retval = locks_verify_area((type == VERIFY_READ) ?
-				   FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+	/* VERIFY_WRITE actually means a read, as we write to user space */
+	retval = locks_verify_area((type == VERIFY_WRITE
+				    ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
 				   inode, file, file->f_pos, tot_len);
 	if (retval) {
 		if (iov != iovstack)
@@ -1106,13 +1104,17 @@
 		__put_user(*fdset, ufdset);
 }
 
+#define MAX_SELECT_SECONDS \
+	((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+
 asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x)
 {
-	fd_set_buffer *fds;
+	fd_set_bits fds;
 	struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x);
+	char *bits;
 	unsigned long nn;
 	long timeout;
-	int ret;
+	int ret, size;
 
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	if (tvp) {
@@ -1123,30 +1125,47 @@
 		    || (ret = __get_user(usec, &tvp->tv_usec)))
 			goto out_nofds;
 
-		timeout = (usec + 1000000/HZ - 1) / (1000000/HZ);
-		timeout += sec * HZ;
+		ret = -EINVAL;
+		if(sec < 0 || usec < 0)
+			goto out_nofds;
+
+		if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+			timeout = (usec + 1000000/HZ - 1) / (1000000/HZ);
+			timeout += sec * (unsigned long) HZ;
+		}
 	}
 
+	ret = -EINVAL;
+	if (n < 0 || n > KFDS_NR)
+		goto out_nofds;
+
+	/*
+	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+	 * since we used fdset we need to allocate memory in units of
+	 * long-words. 
+	 */
 	ret = -ENOMEM;
-	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
-	if (!fds)
+	size = FDS_BYTES(n);
+	bits = kmalloc(6 * size, GFP_KERNEL);
+	if (!bits)
 		goto out_nofds;
-	ret = -EINVAL;
-	if (n < 0)
-		goto out;
-	if (n > KFDS_NR)
-		n = KFDS_NR;
+	fds.in      = (unsigned long *)  bits;
+	fds.out     = (unsigned long *) (bits +   size);
+	fds.ex      = (unsigned long *) (bits + 2*size);
+	fds.res_in  = (unsigned long *) (bits + 3*size);
+	fds.res_out = (unsigned long *) (bits + 4*size);
+	fds.res_ex  = (unsigned long *) (bits + 5*size);
 
 	nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
-	if ((ret = get_fd_set32(nn, fds->in, inp)) ||
-	    (ret = get_fd_set32(nn, fds->out, outp)) ||
-	    (ret = get_fd_set32(nn, fds->ex, exp)))
+	if ((ret = get_fd_set32(nn, fds.in, inp)) ||
+	    (ret = get_fd_set32(nn, fds.out, outp)) ||
+	    (ret = get_fd_set32(nn, fds.ex, exp)))
 		goto out;
-	zero_fd_set(n, fds->res_in);
-	zero_fd_set(n, fds->res_out);
-	zero_fd_set(n, fds->res_ex);
+	zero_fd_set(n, fds.res_in);
+	zero_fd_set(n, fds.res_out);
+	zero_fd_set(n, fds.res_ex);
 
-	ret = do_select(n, fds, &timeout);
+	ret = do_select(n, &fds, &timeout);
 
 	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
 		time_t sec = 0, usec = 0;
@@ -1168,12 +1187,12 @@
 		ret = 0;
 	}
 
-	set_fd_set32(nn, inp, fds->res_in);
-	set_fd_set32(nn, outp, fds->res_out);
-	set_fd_set32(nn, exp, fds->res_ex);
+	set_fd_set32(nn, inp, fds.res_in);
+	set_fd_set32(nn, outp, fds.res_out);
+	set_fd_set32(nn, exp, fds.res_ex);
 
 out:
-	free_page ((unsigned long)fds);
+	kfree(bits);
 out_nofds:
 	return ret;
 }
@@ -1213,7 +1232,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat (statbuf, &s))
 			return -EFAULT;
 	}
@@ -1235,7 +1254,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newlstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat (statbuf, &s))
 			return -EFAULT;
 	}
@@ -2010,74 +2029,6 @@
 	return ret;
 }
 
-struct timex32 {
-	unsigned int modes;
-	s32 offset;
-	s32 freq;
-	s32 maxerror;
-	s32 esterror;
-	int status;
-	s32 constant;
-	s32 precision;
-	s32 tolerance;
-	struct timeval32 time;
-	s32 tick;
-	s32 ppsfreq;
-	s32 jitter;
-	int shift;
-	s32 stabil;
-	s32 jitcnt;
-	s32 calcnt;
-	s32 errcnt;
-	s32 stbcnt;
-	int  :32; int  :32; int  :32; int  :32;
-	int  :32; int  :32; int  :32; int  :32;
-	int  :32; int  :32; int  :32; int  :32;
-};
-
-extern int do_adjtimex(struct timex *);
-
-asmlinkage int sys32_adjtimex(struct timex32 *txc_p)
-{
-	struct timex t;
-	int ret;
-
-	ret = get_user (t.modes, &txc_p->modes);
-	ret |= __get_user (t.offset, &txc_p->offset);
-	ret |= __get_user (t.freq, &txc_p->freq);
-	ret |= __get_user (t.maxerror, &txc_p->maxerror);
-	ret |= __get_user (t.esterror, &txc_p->esterror);
-	ret |= __get_user (t.status, &txc_p->status);
-	ret |= __get_user (t.constant, &txc_p->constant);
-	ret |= __get_user (t.tick, &txc_p->tick);
-	ret |= __get_user (t.shift, &txc_p->shift);
-	if (ret || (ret = do_adjtimex(&t)))
-		return ret;
-	ret = __put_user (t.modes, &txc_p->modes);
-	ret |= __put_user (t.offset, &txc_p->offset);
-	ret |= __put_user (t.freq, &txc_p->freq);
-	ret |= __put_user (t.maxerror, &txc_p->maxerror);
-	ret |= __put_user (t.esterror, &txc_p->esterror);
-	ret |= __put_user (t.status, &txc_p->status);
-	ret |= __put_user (t.constant, &txc_p->constant);
-	ret |= __put_user (t.precision, &txc_p->precision);
-	ret |= __put_user (t.tolerance, &txc_p->tolerance);
-	ret |= __put_user (t.time.tv_sec, &txc_p->time.tv_sec);
-	ret |= __put_user (t.time.tv_usec, &txc_p->time.tv_usec);
-	ret |= __put_user (t.tick, &txc_p->tick);
-	ret |= __put_user (t.ppsfreq, &txc_p->ppsfreq);
-	ret |= __put_user (t.jitter, &txc_p->jitter);
-	ret |= __put_user (t.shift, &txc_p->shift);
-	ret |= __put_user (t.stabil, &txc_p->stabil);
-	ret |= __put_user (t.jitcnt, &txc_p->jitcnt);
-	ret |= __put_user (t.calcnt, &txc_p->calcnt);
-	ret |= __put_user (t.errcnt, &txc_p->errcnt);
-	ret |= __put_user (t.stbcnt, &txc_p->stbcnt);
-	if (!ret)
-		ret = time_state;
-	return ret;
-}
-
 /* XXX This really belongs in some header file... -DaveM */
 #define MAX_SOCK_ADDR	128		/* 108 for Unix domain - 
 					   16 for IP, 16 for IPX,
@@ -2131,9 +2082,44 @@
         __kernel_size_t32 cmsg_len;
         int               cmsg_level;
         int               cmsg_type;
-        unsigned char     cmsg_data[0];
 };
 
+/* Bleech... */
+#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen))
+#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen))
+
+#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) )
+
+#define CMSG32_DATA(cmsg)	((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32))))
+#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len))
+#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len))
+
+#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \
+				    (struct cmsghdr32 *)(ctl) : \
+				    (struct cmsghdr32 *)NULL)
+#define CMSG32_FIRSTHDR(msg)	__CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)
+
+__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size,
+					      struct cmsghdr32 *__cmsg, int __cmsg_len)
+{
+	struct cmsghdr32 * __ptr;
+
+	__ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) +
+				     CMSG32_ALIGN(__cmsg_len));
+	if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
+		return NULL;
+
+	return __ptr;
+}
+
+__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg,
+					    struct cmsghdr32 *__cmsg,
+					    int __cmsg_len)
+{
+	return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen,
+			       __cmsg, __cmsg_len);
+}
+
 static inline int iov_from_user32_to_kern(struct iovec *kiov,
 					  struct iovec32 *uiov32,
 					  int niov)
@@ -2175,6 +2161,7 @@
 	kmsg->msg_control = (void *)A(tmp3);
 
 	err = get_user(kmsg->msg_namelen, &umsg->msg_namelen);
+	err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen);
 	err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen);
 	err |= get_user(kmsg->msg_flags, &umsg->msg_flags);
 	
@@ -2217,6 +2204,165 @@
 	return tot_len;
 }
 
+/* There is a lot of hair here because the alignment rules (and
+ * thus placement) of cmsg headers and length are different for
+ * 32-bit apps.  -DaveM
+ */
+static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg,
+				       unsigned char *stackbuf, int stackbuf_size)
+{
+	struct cmsghdr32 *ucmsg;
+	struct cmsghdr *kcmsg, *kcmsg_base;
+	__kernel_size_t32 ucmlen;
+	__kernel_size_t kcmlen, tmp;
+
+	kcmlen = 0;
+	kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
+	ucmsg = CMSG32_FIRSTHDR(kmsg);
+	while(ucmsg != NULL) {
+		if(get_user(ucmlen, &ucmsg->cmsg_len))
+			return -EFAULT;
+
+		/* Catch bogons. */
+		if(CMSG32_ALIGN(ucmlen) <
+		   CMSG32_ALIGN(sizeof(struct cmsghdr32)))
+			return -EINVAL;
+		if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control)
+				   + ucmlen) > kmsg->msg_controllen)
+			return -EINVAL;
+
+		tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+		       CMSG_ALIGN(sizeof(struct cmsghdr)));
+		kcmlen += tmp;
+		ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+	}
+	if(kcmlen == 0)
+		return -EINVAL;
+
+	/* The kcmlen holds the 64-bit version of the control length.
+	 * It may not be modified as we do not stick it into the kmsg
+	 * until we have successfully copied over all of the data
+	 * from the user.
+	 */
+	if(kcmlen > stackbuf_size)
+		kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL);
+	if(kcmsg == NULL)
+		return -ENOBUFS;
+
+	/* Now copy them over neatly. */
+	memset(kcmsg, 0, kcmlen);
+	ucmsg = CMSG32_FIRSTHDR(kmsg);
+	while(ucmsg != NULL) {
+		__get_user(ucmlen, &ucmsg->cmsg_len);
+		tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+		       CMSG_ALIGN(sizeof(struct cmsghdr)));
+		kcmsg->cmsg_len = tmp;
+		__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level);
+		__get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type);
+
+		/* Copy over the data. */
+		if(copy_from_user(CMSG_DATA(kcmsg),
+				  CMSG32_DATA(ucmsg),
+				  (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg)))))
+			goto out_free_efault;
+
+		/* Advance. */
+		kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp));
+		ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+	}
+
+	/* Ok, looks like we made it.  Hook it up and return success. */
+	kmsg->msg_control = kcmsg_base;
+	kmsg->msg_controllen = kcmlen;
+	return 0;
+
+out_free_efault:
+	if(kcmsg_base != (struct cmsghdr *)stackbuf)
+		kfree(kcmsg_base);
+	return -EFAULT;
+}
+
+static void put_cmsg32(struct msghdr *kmsg, int level, int type,
+		       int len, void *data)
+{
+	struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+	struct cmsghdr32 cmhdr;
+	int cmlen = CMSG32_LEN(len);
+
+	if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
+		kmsg->msg_flags |= MSG_CTRUNC;
+		return;
+	}
+
+	if(kmsg->msg_controllen < cmlen) {
+		kmsg->msg_flags |= MSG_CTRUNC;
+		cmlen = kmsg->msg_controllen;
+	}
+	cmhdr.cmsg_level = level;
+	cmhdr.cmsg_type = type;
+	cmhdr.cmsg_len = cmlen;
+
+	if(copy_to_user(cm, &cmhdr, sizeof cmhdr))
+		return;
+	if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32)))
+		return;
+	cmlen = CMSG32_SPACE(len);
+	kmsg->msg_control += cmlen;
+	kmsg->msg_controllen -= cmlen;
+}
+
+static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm)
+{
+	struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+	int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int);
+	int fdnum = scm->fp->count;
+	struct file **fp = scm->fp->fp;
+	int *cmfptr;
+	int err = 0, i;
+
+	if (fdnum < fdmax)
+		fdmax = fdnum;
+
+	for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) {
+		int new_fd;
+		err = get_unused_fd();
+		if (err < 0)
+			break;
+		new_fd = err;
+		err = put_user(new_fd, cmfptr);
+		if (err) {
+			put_unused_fd(new_fd);
+			break;
+		}
+		/* Bump the usage count and install the file. */
+		fp[i]->f_count++;
+		current->files->fd[new_fd] = fp[i];
+	}
+
+	if (i > 0) {
+		int cmlen = CMSG32_LEN(i * sizeof(int));
+		if (!err)
+			err = put_user(SOL_SOCKET, &cm->cmsg_level);
+		if (!err)
+			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
+		if (!err)
+			err = put_user(cmlen, &cm->cmsg_len);
+		if (!err) {
+			cmlen = CMSG32_SPACE(i * sizeof(int));
+			kmsg->msg_control += cmlen;
+			kmsg->msg_controllen -= cmlen;
+		}
+	}
+	if (i < fdnum)
+		kmsg->msg_flags |= MSG_CTRUNC;
+
+	/*
+	 * All of the files that fit in the message have had their
+	 * usage counts incremented, so we just free the list.
+	 */
+	__scm_destroy(scm);
+}
+
 asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags)
 {
 	struct socket *sock;
@@ -2237,25 +2383,10 @@
 	total_len = err;
 
 	if(kern_msg.msg_controllen) {
-		struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control;
-		unsigned long *kcmsg;
-		__kernel_size_t32 cmlen;
-
-		if(kern_msg.msg_controllen > sizeof(ctl) &&
-		   kern_msg.msg_controllen <= 256) {
-			err = -ENOBUFS;
-			ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL);
-			if(!ctl_buf)
-				goto out_freeiov;
-		}
-		__get_user(cmlen, &ucmsg->cmsg_len);
-		kcmsg = (unsigned long *) ctl_buf;
-		*kcmsg++ = (unsigned long)cmlen;
-		err = -EFAULT;
-		if(copy_from_user(kcmsg, &ucmsg->cmsg_level,
-				  kern_msg.msg_controllen - sizeof(__kernel_size_t32)))
-			goto out_freectl;
-		kern_msg.msg_control = ctl_buf;
+		err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl));
+		if(err)
+			goto out_freeiov;
+		ctl_buf = kern_msg.msg_control;
 	}
 	kern_msg.msg_flags = user_flags;
 
@@ -2269,7 +2400,6 @@
 	}
 	unlock_kernel();
 
-out_freectl:
 	/* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
 	if(ctl_buf != ctl)
 		kfree(ctl_buf);
@@ -2310,26 +2440,43 @@
 	lock_kernel();
 	sock = sockfd_lookup(fd, &err);
 	if (sock != NULL) {
+		struct scm_cookie scm;
+
 		if (sock->file->f_flags & O_NONBLOCK)
 			user_flags |= MSG_DONTWAIT;
-		err = sock_recvmsg(sock, &kern_msg, total_len, user_flags);
-		if(err >= 0)
+		memset(&scm, 0, sizeof(scm));
+		err = sock->ops->recvmsg(sock, &kern_msg, total_len,
+					 user_flags, &scm);
+		if(err >= 0) {
 			len = err;
+			if(!kern_msg.msg_control) {
+				if(sock->passcred || scm.fp)
+					kern_msg.msg_flags |= MSG_CTRUNC;
+				if(scm.fp)
+					__scm_destroy(&scm);
+			} else {
+				/* Wheee... */
+				if(sock->passcred)
+					put_cmsg32(&kern_msg,
+						   SOL_SOCKET, SCM_CREDENTIALS,
+						   sizeof(scm.creds), &scm.creds);
+				if(scm.fp != NULL)
+					scm_detach_fds32(&kern_msg, &scm);
+			}
+		}
 		sockfd_put(sock);
 	}
 	unlock_kernel();
 
 	if(uaddr != NULL && err >= 0)
 		err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
-	if(err >= 0) {
-		err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
-		if(!err) {
-			/* XXX Convert cmsg back into userspace 32-bit format... */
-			err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr,
-					 &user_msg->msg_controllen);
-		}
+	if(cmsg_ptr != 0 && err >= 0) {
+		u32 ucmsg_ptr = ((u32)(unsigned long)kern_msg.msg_control);
+		err  = __put_user(ucmsg_ptr, &user_msg->msg_control);
+		err |= __put_user(kern_msg.msg_controllen, &user_msg->msg_controllen);
 	}
-
+	if(err >= 0)
+		err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
 	if(kern_msg.msg_iov != iov)
 		kfree(kern_msg.msg_iov);
 out:
@@ -2653,7 +2800,7 @@
         error = do_execve32(filename,
         	(u32 *)AA((u32)regs->u_regs[base + UREG_I1]),
         	(u32 *)AA((u32)regs->u_regs[base + UREG_I2]), regs);
-        putname32(filename);
+        putname(filename);
 
 	if(!error) {
 		fprs_write(0);
@@ -2943,8 +3090,10 @@
 		info.addr = (unsigned long)mod;
 		info.size = mod->size;
 		info.flags = mod->flags;
-		info.usecount = (mod_member_present(mod, can_unload)
-				 && mod->can_unload ? -1 : mod->usecount);
+		info.usecount =
+			((mod_member_present(mod, can_unload)
+			  && mod->can_unload)
+			 ? -1 : atomic_read(&mod->uc.usecount));
 
 		if (copy_to_user(buf, &info, sizeof(struct module_info32)))
 			return -EFAULT;
@@ -3452,7 +3601,7 @@
 		ret = sys_utimes(kfilename, &ktvs[0]);
 		set_fs(old_fs);
 
-		putname32(kfilename);
+		putname(kfilename);
 	}
 	return ret;
 }
@@ -3575,5 +3724,78 @@
 	if (!ret && offset && put_user(of, offset))
 		return -EFAULT;
 		
+	return ret;
+}
+
+/* Handle adjtimex compatability. */
+
+struct timex32 {
+	u32 modes;
+	s32 offset, freq, maxerror, esterror;
+	s32 status, constant, precision, tolerance;
+	struct timeval32 time;
+	s32 tick;
+	s32 ppsfreq, jitter, shift, stabil;
+	s32 jitcnt, calcnt, errcnt, stbcnt;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+};
+
+extern int do_adjtimex(struct timex *);
+
+asmlinkage int sys32_adjtimex(struct timex32 *utp)
+{
+	struct timex txc;
+	int ret;
+
+	memset(&txc, 0, sizeof(struct timex));
+
+	if(get_user(txc.modes, &utp->modes) ||
+	   __get_user(txc.offset, &utp->offset) ||
+	   __get_user(txc.freq, &utp->freq) ||
+	   __get_user(txc.maxerror, &utp->maxerror) ||
+	   __get_user(txc.esterror, &utp->esterror) ||
+	   __get_user(txc.status, &utp->status) ||
+	   __get_user(txc.constant, &utp->constant) ||
+	   __get_user(txc.precision, &utp->precision) ||
+	   __get_user(txc.tolerance, &utp->tolerance) ||
+	   __get_user(txc.time.tv_sec, &utp->time.tv_sec) ||
+	   __get_user(txc.time.tv_usec, &utp->time.tv_usec) ||
+	   __get_user(txc.tick, &utp->tick) ||
+	   __get_user(txc.ppsfreq, &utp->ppsfreq) ||
+	   __get_user(txc.jitter, &utp->jitter) ||
+	   __get_user(txc.shift, &utp->shift) ||
+	   __get_user(txc.stabil, &utp->stabil) ||
+	   __get_user(txc.jitcnt, &utp->jitcnt) ||
+	   __get_user(txc.calcnt, &utp->calcnt) ||
+	   __get_user(txc.errcnt, &utp->errcnt) ||
+	   __get_user(txc.stbcnt, &utp->stbcnt))
+		return -EFAULT;
+
+	ret = do_adjtimex(&txc);
+
+	if(put_user(txc.modes, &utp->modes) ||
+	   __put_user(txc.offset, &utp->offset) ||
+	   __put_user(txc.freq, &utp->freq) ||
+	   __put_user(txc.maxerror, &utp->maxerror) ||
+	   __put_user(txc.esterror, &utp->esterror) ||
+	   __put_user(txc.status, &utp->status) ||
+	   __put_user(txc.constant, &utp->constant) ||
+	   __put_user(txc.precision, &utp->precision) ||
+	   __put_user(txc.tolerance, &utp->tolerance) ||
+	   __put_user(txc.time.tv_sec, &utp->time.tv_sec) ||
+	   __put_user(txc.time.tv_usec, &utp->time.tv_usec) ||
+	   __put_user(txc.tick, &utp->tick) ||
+	   __put_user(txc.ppsfreq, &utp->ppsfreq) ||
+	   __put_user(txc.jitter, &utp->jitter) ||
+	   __put_user(txc.shift, &utp->shift) ||
+	   __put_user(txc.stabil, &utp->stabil) ||
+	   __put_user(txc.jitcnt, &utp->jitcnt) ||
+	   __put_user(txc.calcnt, &utp->calcnt) ||
+	   __put_user(txc.errcnt, &utp->errcnt) ||
+	   __put_user(txc.stbcnt, &utp->stbcnt))
+		ret = -EFAULT;
+
 	return ret;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)