2223 lines · 52.5 KB · C
RawDownload
/* *  linux/fs/compat.c * *  Kernel compatibililty routines for e.g. 32 bit syscall support *  on 64 bit kernels. * *  Copyright (C) 2002       Stephen Rothwell, IBM Corporation *  Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com) *  Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be) *  Copyright (C) 2001,2002  Andi Kleen, SuSE Labs  *  Copyright (C) 2003       Pavel Machek (pavel@suse.cz) * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */ #include <linux/linkage.h>#include <linux/compat.h>#include <linux/errno.h>#include <linux/time.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/namei.h>#include <linux/file.h>#include <linux/vfs.h>#include <linux/ioctl32.h>#include <linux/ioctl.h>#include <linux/init.h>#include <linux/sockios.h>	/* for SIOCDEVPRIVATE */#include <linux/smb.h>#include <linux/smb_mount.h>#include <linux/ncp_mount.h>#include <linux/nfs4_mount.h>#include <linux/smp_lock.h>#include <linux/syscalls.h>#include <linux/ctype.h>#include <linux/module.h>#include <linux/dirent.h>#include <linux/fsnotify.h>#include <linux/highuid.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/syscall.h>#include <linux/personality.h>#include <linux/rwsem.h>#include <linux/acct.h>#include <linux/mm.h> #include <net/sock.h>		/* siocdevprivate_ioctl */ #include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/ioctls.h> extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t){	struct timeval tv[2]; 	if (t) {		if (get_user(tv[0].tv_sec, &t->actime) ||		    get_user(tv[1].tv_sec, &t->modtime))			return -EFAULT;		tv[0].tv_usec = 0;		tv[1].tv_usec = 0;	}	return do_utimes(AT_FDCWD, filename, t ? tv : NULL);} asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t){	struct timeval tv[2]; 	if (t) {		if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||		    get_user(tv[0].tv_usec, &t[0].tv_usec) ||		    get_user(tv[1].tv_sec, &t[1].tv_sec) ||		    get_user(tv[1].tv_usec, &t[1].tv_usec))			return -EFAULT;	}	return do_utimes(dfd, filename, t ? tv : NULL);} asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t){	return compat_sys_futimesat(AT_FDCWD, filename, t);} asmlinkage long compat_sys_newstat(char __user * filename,		struct compat_stat __user *statbuf){	struct kstat stat;	int error = vfs_stat_fd(AT_FDCWD, filename, &stat); 	if (!error)		error = cp_compat_stat(&stat, statbuf);	return error;} asmlinkage long compat_sys_newlstat(char __user * filename,		struct compat_stat __user *statbuf){	struct kstat stat;	int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); 	if (!error)		error = cp_compat_stat(&stat, statbuf);	return error;} #ifndef __ARCH_WANT_STAT64asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename,		struct compat_stat __user *statbuf, int flag){	struct kstat stat;	int error = -EINVAL; 	if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)		goto out; 	if (flag & AT_SYMLINK_NOFOLLOW)		error = vfs_lstat_fd(dfd, filename, &stat);	else		error = vfs_stat_fd(dfd, filename, &stat); 	if (!error)		error = cp_compat_stat(&stat, statbuf); out:	return error;}#endif asmlinkage long compat_sys_newfstat(unsigned int fd,		struct compat_stat __user * statbuf){	struct kstat stat;	int error = vfs_fstat(fd, &stat); 	if (!error)		error = cp_compat_stat(&stat, statbuf);	return error;} static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf){		if (sizeof ubuf->f_blocks == 4) {		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &		    0xffffffff00000000ULL)			return -EOVERFLOW;		/* f_files and f_ffree may be -1; it's okay		 * to stuff that into 32 bits */		if (kbuf->f_files != 0xffffffffffffffffULL		 && (kbuf->f_files & 0xffffffff00000000ULL))			return -EOVERFLOW;		if (kbuf->f_ffree != 0xffffffffffffffffULL		 && (kbuf->f_ffree & 0xffffffff00000000ULL))			return -EOVERFLOW;	}	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||	    __put_user(kbuf->f_type, &ubuf->f_type) ||	    __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||	    __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||	    __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||	    __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||	    __put_user(kbuf->f_files, &ubuf->f_files) ||	    __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||	    __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||	    __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||	    __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||	    __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||	    __put_user(0, &ubuf->f_spare[0]) || 	    __put_user(0, &ubuf->f_spare[1]) || 	    __put_user(0, &ubuf->f_spare[2]) || 	    __put_user(0, &ubuf->f_spare[3]) || 	    __put_user(0, &ubuf->f_spare[4]))		return -EFAULT;	return 0;} /* * The following statfs calls are copies of code from fs/open.c and * should be checked against those from time to time */asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf){	struct nameidata nd;	int error; 	error = user_path_walk(path, &nd);	if (!error) {		struct kstatfs tmp;		error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);		if (!error)			error = put_compat_statfs(buf, &tmp);		path_release(&nd);	}	return error;} asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf){	struct file * file;	struct kstatfs tmp;	int error; 	error = -EBADF;	file = fget(fd);	if (!file)		goto out;	error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);	if (!error)		error = put_compat_statfs(buf, &tmp);	fput(file);out:	return error;} static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf){	if (sizeof ubuf->f_blocks == 4) {		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) &		    0xffffffff00000000ULL)			return -EOVERFLOW;		/* f_files and f_ffree may be -1; it's okay		 * to stuff that into 32 bits */		if (kbuf->f_files != 0xffffffffffffffffULL		 && (kbuf->f_files & 0xffffffff00000000ULL))			return -EOVERFLOW;		if (kbuf->f_ffree != 0xffffffffffffffffULL		 && (kbuf->f_ffree & 0xffffffff00000000ULL))			return -EOVERFLOW;	}	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||	    __put_user(kbuf->f_type, &ubuf->f_type) ||	    __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||	    __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||	    __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||	    __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||	    __put_user(kbuf->f_files, &ubuf->f_files) ||	    __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||	    __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||	    __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||	    __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||	    __put_user(kbuf->f_frsize, &ubuf->f_frsize))		return -EFAULT;	return 0;} asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf){	struct nameidata nd;	int error; 	if (sz != sizeof(*buf))		return -EINVAL; 	error = user_path_walk(path, &nd);	if (!error) {		struct kstatfs tmp;		error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);		if (!error)			error = put_compat_statfs64(buf, &tmp);		path_release(&nd);	}	return error;} asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf){	struct file * file;	struct kstatfs tmp;	int error; 	if (sz != sizeof(*buf))		return -EINVAL; 	error = -EBADF;	file = fget(fd);	if (!file)		goto out;	error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);	if (!error)		error = put_compat_statfs64(buf, &tmp);	fput(file);out:	return error;} /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */ #define IOCTL_HASHSIZE 256static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; extern struct ioctl_trans ioctl_start[];extern int ioctl_table_size; static inline unsigned long ioctl32_hash(unsigned long cmd){	return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;} static void ioctl32_insert_translation(struct ioctl_trans *trans){	unsigned long hash;	struct ioctl_trans *t; 	hash = ioctl32_hash (trans->cmd);	if (!ioctl32_hash_table[hash])		ioctl32_hash_table[hash] = trans;	else {		t = ioctl32_hash_table[hash];		while (t->next)			t = t->next;		trans->next = NULL;		t->next = trans;	}} static int __init init_sys32_ioctl(void){	int i; 	for (i = 0; i < ioctl_table_size; i++) {		if (ioctl_start[i].next != 0) { 			printk("ioctl translation %d bad\n",i); 			return -1;		} 		ioctl32_insert_translation(&ioctl_start[i]);	}	return 0;} __initcall(init_sys32_ioctl); static void compat_ioctl_error(struct file *filp, unsigned int fd,		unsigned int cmd, unsigned long arg){	char buf[10];	char *fn = "?";	char *path; 	/* find the name of the device. */	path = (char *)__get_free_page(GFP_KERNEL);	if (path) {		fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE);		if (IS_ERR(fn))			fn = "?";	} 	sprintf(buf,"'%c'", (cmd>>24) & 0x3f);	if (!isprint(buf[1]))		sprintf(buf, "%02x", buf[1]);	printk("ioctl32(%s:%d): Unknown cmd fd(%d) "			"cmd(%08x){%s} arg(%08x) on %s\n",			current->comm, current->pid,			(int)fd, (unsigned int)cmd, buf,			(unsigned int)arg, fn); 	if (path)		free_page((unsigned long)path);} asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,				unsigned long arg){	struct file *filp;	int error = -EBADF;	struct ioctl_trans *t;	int fput_needed; 	filp = fget_light(fd, &fput_needed);	if (!filp)		goto out; 	/* RED-PEN how should LSM module know it's handling 32bit? */	error = security_file_ioctl(filp, cmd, arg);	if (error)		goto out_fput; 	/*	 * To allow the compat_ioctl handlers to be self contained	 * we need to check the common ioctls here first.	 * Just handle them with the standard handlers below.	 */	switch (cmd) {	case FIOCLEX:	case FIONCLEX:	case FIONBIO:	case FIOASYNC:	case FIOQSIZE:		break; 	case FIBMAP:	case FIGETBSZ:	case FIONREAD:		if (S_ISREG(filp->f_dentry->d_inode->i_mode))			break;		/*FALL THROUGH*/ 	default:		if (filp->f_op && filp->f_op->compat_ioctl) {			error = filp->f_op->compat_ioctl(filp, cmd, arg);			if (error != -ENOIOCTLCMD)				goto out_fput;		} 		if (!filp->f_op ||		    (!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl))			goto do_ioctl;		break;	} 	for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) {		if (t->cmd == cmd)			goto found_handler;	} 	if (S_ISSOCK(filp->f_dentry->d_inode->i_mode) &&	    cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {		error = siocdevprivate_ioctl(fd, cmd, arg);	} else {		static int count; 		if (++count <= 50)			compat_ioctl_error(filp, fd, cmd, arg);		error = -EINVAL;	} 	goto out_fput;  found_handler:	if (t->handler) {		lock_kernel();		error = t->handler(fd, cmd, arg, filp);		unlock_kernel();		goto out_fput;	}  do_ioctl:	error = vfs_ioctl(filp, fd, cmd, arg); out_fput:	fput_light(filp, fput_needed); out:	return error;} static int get_compat_flock(struct flock *kfl, struct compat_flock __user *ufl){	if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||	    __get_user(kfl->l_type, &ufl->l_type) ||	    __get_user(kfl->l_whence, &ufl->l_whence) ||	    __get_user(kfl->l_start, &ufl->l_start) ||	    __get_user(kfl->l_len, &ufl->l_len) ||	    __get_user(kfl->l_pid, &ufl->l_pid))		return -EFAULT;	return 0;} static int put_compat_flock(struct flock *kfl, struct compat_flock __user *ufl){	if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||	    __put_user(kfl->l_type, &ufl->l_type) ||	    __put_user(kfl->l_whence, &ufl->l_whence) ||	    __put_user(kfl->l_start, &ufl->l_start) ||	    __put_user(kfl->l_len, &ufl->l_len) ||	    __put_user(kfl->l_pid, &ufl->l_pid))		return -EFAULT;	return 0;} #ifndef HAVE_ARCH_GET_COMPAT_FLOCK64static int get_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl){	if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||	    __get_user(kfl->l_type, &ufl->l_type) ||	    __get_user(kfl->l_whence, &ufl->l_whence) ||	    __get_user(kfl->l_start, &ufl->l_start) ||	    __get_user(kfl->l_len, &ufl->l_len) ||	    __get_user(kfl->l_pid, &ufl->l_pid))		return -EFAULT;	return 0;}#endif #ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl){	if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||	    __put_user(kfl->l_type, &ufl->l_type) ||	    __put_user(kfl->l_whence, &ufl->l_whence) ||	    __put_user(kfl->l_start, &ufl->l_start) ||

Lines 1–500 of 2223.