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

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

diff -u --recursive --new-file v2.1.41/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.13 1997/05/18 04:16:44 davem Exp $
+/* $Id: sys_sparc32.c,v 1.18 1997/05/27 06:28:08 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -48,7 +48,6 @@
 extern asmlinkage int sys_bdflush(int func, long data);
 extern asmlinkage int sys_uselib(const char * library);
 extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg);
-extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
 extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev);
 extern asmlinkage int sys_mkdir(const char * pathname, int mode);
 extern asmlinkage int sys_rmdir(const char * pathname);
@@ -147,6 +146,69 @@
 extern asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2]);
 extern asmlinkage int sys_shutdown(int fd, int how);
 
+/*
+ * 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..
+ *
+ * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ */
+static inline int do_getname32(u32 filename, char *page)
+{
+	int retval;
+
+	/* 32bit pointer will be always far below TASK_SIZE :)) */
+	retval = strncpy_from_user((char *)page, (char *)A(filename), PAGE_SIZE);
+	if (retval > 0) {
+		if (retval < PAGE_SIZE)
+			return 0;
+		return -ENAMETOOLONG;
+	} else if (!retval)
+		retval = -ENOENT;
+	return retval;
+}
+
+/*
+ * This is a single page for faster getname.
+ *   If the page is available when entering getname, use it.
+ *   If the page is not available, call __get_free_page instead.
+ * This works even though do_getname can block (think about it).
+ * -- Michael Chastain, based on idea of Linus Torvalds, 1 Dec 1996.
+ * We don't use the common getname/putname from namei.c, so that
+ * this still works well, as every routine which calls getname32
+ * will then call getname, then putname and then putname32.
+ */
+static unsigned long name_page_cache32 = 0;
+
+void putname32(char * name)
+{
+	if (name_page_cache32 == 0)
+		name_page_cache32 = (unsigned long) name;
+	else
+		free_page((unsigned long) name);
+}
+
+int getname32(u32 filename, char **result)
+{
+	unsigned long page;
+	int retval;
+
+	page = name_page_cache32;
+	name_page_cache32 = 0;
+	if (!page) {
+		page = __get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+	}
+
+	retval = do_getname32(filename, (char *) page);
+	if (retval < 0)
+		putname32( (char *) page );
+	else
+		*result = (char *) page;
+	return retval;
+}
+
 asmlinkage int sys32_ioperm(u32 from, u32 num, int on)
 {
 	return sys_ioperm((unsigned long)from, (unsigned long)num, on);
@@ -558,13 +620,6 @@
 	}
 }
 
-/* Conversion of args should be probably done in all the locations where it is handled,
-   using if (current->tss.flags & SPARC_FLAG_32BIT */
-asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
-{
-	return sys_ioctl(fd, cmd, (unsigned long)arg);
-}
-
 asmlinkage int sys32_mknod(u32 filename, int mode, __kernel_dev_t32 dev)
 {
 	return sys_mknod((const char *)A(filename), mode, dev);
@@ -704,14 +759,21 @@
 	struct utimbuf t;
 	unsigned long old_fs;
 	int ret;
+	char *filenam;
 	
+	if (!times)
+		return sys_utime((char *)A(filename), NULL);
 	if (get_user (t.actime, &(((struct utimbuf32 *)A(times))->actime)) ||
 	    __get_user (t.modtime, &(((struct utimbuf32 *)A(times))->modtime)))
 		return -EFAULT;
-	old_fs = get_fs();
-	set_fs (KERNEL_DS); 
-	ret = sys_utime((char *)A(filename), &t);
-	set_fs (old_fs);
+	ret = getname32 (filename, &filenam);
+	if (!ret) {
+		old_fs = get_fs();
+		set_fs (KERNEL_DS); 
+		ret = sys_utime(filenam, &t);
+		set_fs (old_fs);
+		putname32 (filenam);
+	}
 	return ret;
 }
 
@@ -992,6 +1054,7 @@
 
 asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
 {
+	struct timeval kern_tv, *ktvp;
 	unsigned long old_fs;
 	char *p;
 	u32 *q;
@@ -1015,9 +1078,15 @@
 		    __get_user (q[PAGE_SIZE/2], Exp+1))
 			goto out;
 	}
+	ktvp = NULL;
+	if(tvp) {
+		if(copy_from_user(&kern_tv, (struct timeval *)A(tvp), sizeof(*ktvp)))
+			goto out;
+		ktvp = &kern_tv;
+	}
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
-	ret = sys_select(n, (fd_set *)p, (fd_set *)(p + PAGE_SIZE/4), (fd_set *)(p + PAGE_SIZE/2), (struct timeval *)A(tvp));
+	ret = sys_select(n, (fd_set *)p, (fd_set *)(p + PAGE_SIZE/4), (fd_set *)(p + PAGE_SIZE/2), ktvp);
 	set_fs (old_fs);
 	q = (u32 *)p;
 	Inp = (u32 *)A(inp); Outp = (u32 *)A(outp); Exp = (u32 *)A(exp);
@@ -1065,12 +1134,17 @@
 {
 	int ret;
 	struct stat s;
+	char *filenam;
 	unsigned long old_fs = get_fs();
 	
-	set_fs (KERNEL_DS);
-	ret = sys_newstat((char *)A(filename), &s);
-	set_fs (old_fs);
-	if (putstat (statbuf, &s)) return -EFAULT;
+	ret = getname32 (filename, &filenam);
+	if (!ret) {
+		set_fs (KERNEL_DS);
+		ret = sys_newstat(filenam, &s);
+		set_fs (old_fs);
+		putname32 (filenam);
+		if (putstat (statbuf, &s)) return -EFAULT;
+	}
 	return ret;
 }
 
@@ -1078,12 +1152,17 @@
 {
 	int ret;
 	struct stat s;
+	char *filenam;
 	unsigned long old_fs = get_fs();
 	
-	set_fs (KERNEL_DS);
-	ret = sys_newlstat((char *)A(filename), &s);
-	set_fs (old_fs);
-	if (putstat (statbuf, &s)) return -EFAULT;
+	ret = getname32 (filename, &filenam);
+	if (!ret) {
+		set_fs (KERNEL_DS);
+		ret = sys_newlstat(filenam, &s);
+		set_fs (old_fs);
+		putname32 (filenam);
+		if (putstat (statbuf, &s)) return -EFAULT;
+	}
 	return ret;
 }
 
@@ -1985,6 +2064,144 @@
 {
 	/* XXX handle argp and resp args */
 	return sys_nfsservctl(cmd, (void *)A(argp), (void *)A(resp));
+}
+
+/*
+ * count32() counts the number of arguments/envelopes
+ */
+static int count32(u32 * argv)
+{
+	int i = 0;
+
+	if (argv != NULL) {
+		for (;;) {
+			u32 p; int error;
+
+			error = get_user(p,argv);
+			if (error) return error;
+			if (!p) break;
+			argv++; i++;
+		}
+	}
+	return i;
+}
+
+/*
+ * 'copy_string32()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ */
+static unsigned long
+copy_strings32(int argc,u32 * argv,unsigned long *page,
+	       unsigned long p)
+{
+	u32 str;
+
+	if (!p) return 0;	/* bullet-proofing */
+	while (argc-- > 0) {
+		int len;
+		unsigned long pos;
+
+		get_user(str, argv+argc);
+		if (!str) panic("VFS: argc is wrong");
+		len = strlen_user((char *)A(str));	/* includes the '\0' */
+		if (p < len)	/* this shouldn't happen - 128kB */
+			return 0;
+		p -= len; pos = p;
+		while (len) {
+			char *pag;
+			int offset, bytes_to_copy;
+
+			offset = pos % PAGE_SIZE;
+			if (!(pag = (char *) page[pos/PAGE_SIZE]) &&
+			    !(pag = (char *) page[pos/PAGE_SIZE] =
+			      (unsigned long *) get_free_page(GFP_USER)))
+				return 0;
+			bytes_to_copy = PAGE_SIZE - offset;
+			if (bytes_to_copy > len)
+				bytes_to_copy = len;
+			copy_from_user(pag + offset, (char *)A(str), bytes_to_copy);
+			pos += bytes_to_copy;
+			str += bytes_to_copy;
+			len -= bytes_to_copy;
+		}
+	}
+	return p;
+}
+
+/*
+ * sys32_execve() executes a new program.
+ */
+static inline int 
+do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
+{
+	struct linux_binprm bprm;
+	int retval;
+	int i;
+
+	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+	for (i=0 ; i<MAX_ARG_PAGES ; i++)	/* clear page-table */
+		bprm.page[i] = 0;
+	retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
+	if (retval)
+		return retval;
+	bprm.filename = filename;
+	bprm.sh_bang = 0;
+	bprm.java = 0;
+	bprm.loader = 0;
+	bprm.exec = 0;
+	bprm.dont_iput = 0;
+	if ((bprm.argc = count32(argv)) < 0)
+		return bprm.argc;
+	if ((bprm.envc = count32(envp)) < 0)
+		return bprm.envc;
+
+	retval = prepare_binprm(&bprm);
+	
+	if(retval>=0) {
+		bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
+		bprm.exec = bprm.p;
+		bprm.p = copy_strings32(bprm.envc,envp,bprm.page,bprm.p);
+		bprm.p = copy_strings32(bprm.argc,argv,bprm.page,bprm.p);
+		if (!bprm.p)
+			retval = -E2BIG;
+	}
+
+	if(retval>=0)
+		retval = search_binary_handler(&bprm,regs);
+	if(retval>=0)
+		/* execve success */
+		return retval;
+
+	/* Something went wrong, return the inode and free the argument pages*/
+	if(!bprm.dont_iput)
+		iput(bprm.inode);
+	for (i=0 ; i<MAX_ARG_PAGES ; i++)
+		free_page(bprm.page[i]);
+	return(retval);
+}
+
+/*
+ * sparc32_execve() executes a new program after the asm stub has set
+ * things up for us.  This should basically do what I want it to.
+ */
+asmlinkage int sparc32_execve(struct pt_regs *regs)
+{
+        int error, base = 0;
+        char *filename;
+
+        /* Check for indirect call. */
+        if((u32)regs->u_regs[UREG_G1] == 0)
+                base = 1;
+
+        error = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0], &filename);
+        if(error)
+                return error;
+        error = do_execve32(filename,
+        	(u32 *)A((u32)regs->u_regs[base + UREG_I1]),
+        	(u32 *)A((u32)regs->u_regs[base + UREG_I2]), regs);
+        putname(filename);
+        return error;
 }
 
 struct ncp_mount_data32 {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov