📄 sys_ia32.c
字号:
/* * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on * sys_sparc32 * * Copyright (C) 2000 VA Linux Co * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 2000 Hewlett-Packard Co. * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> * * These routines maintain argument size conversion between 32bit and 64bit * environment. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/fs.h> #include <linux/file.h> #include <linux/signal.h>#include <linux/utime.h>#include <linux/resource.h>#include <linux/times.h>#include <linux/utsname.h>#include <linux/timex.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/sem.h>#include <linux/msg.h>#include <linux/mm.h>#include <linux/shm.h>#include <linux/malloc.h>#include <linux/uio.h>#include <linux/nfs_fs.h>#include <linux/smb_fs.h>#include <linux/smb_mount.h>#include <linux/ncp_fs.h>#include <linux/quota.h>#include <linux/module.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/cache.h>#include <linux/nfsd/xdr.h>#include <linux/nfsd/syscall.h>#include <linux/poll.h>#include <linux/personality.h>#include <linux/stat.h>#include <linux/ipc.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#include <asm/ipc.h>#include <net/scm.h>#include <net/sock.h>#include <asm/ia32.h>#define A(__x) ((unsigned long)(__x))#define AA(__x) ((unsigned long)(__x))#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1)))#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))extern asmlinkage long sys_execve (char *, char **, char **, struct pt_regs *);extern asmlinkage long sys_munmap (unsigned long, size_t len);extern asmlinkage long sys_mprotect (unsigned long, size_t, unsigned long);static intnargs(unsigned int arg, char **ap){ int n, err, addr; n = 0; do { err = get_user(addr, (int *)A(arg)); if (err) return err; if (ap) { /* no access_ok needed, we allocated */ err = __put_user((char *)A(addr), ap++); if (err) return err; } arg += sizeof(unsigned int); n++; } while (addr); return(n - 1);}asmlinkage longsys32_execve(char *filename,unsigned int argv,unsigned int envp,int dummy3,int dummy4,int dummy5,int dummy6,int dummy7,int stack){ struct pt_regs *regs = (struct pt_regs *)&stack; char **av, **ae; int na, ne, len; long r; na = nargs(argv, NULL); if (na < 0) return(na); ne = nargs(envp, NULL); if (ne < 0) return(ne); len = (na + ne + 2) * sizeof(*av); /* * kmalloc won't work because the `sys_exec' code will attempt * to do a `get_user' on the arg list and `get_user' will fail * on a kernel address (simplifies `get_user'). Instead we * do an mmap to get a user address. Note that since a successful * `execve' frees all current memory we only have to do an * `munmap' if the `execve' failes. */ down(¤t->mm->mmap_sem); av = (char **) do_mmap_pgoff(0, 0UL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0); up(¤t->mm->mmap_sem); if (IS_ERR(av)) return (long)av; ae = av + na + 1; r = __put_user(0, (av + na)); if (r) goto out; r = __put_user(0, (ae + ne)); if (r) goto out; r = nargs(argv, av); if (r < 0) goto out; r = nargs(envp, ae); if (r < 0) goto out; r = sys_execve(filename, av, ae, regs); if (r < 0)out: sys_munmap((unsigned long) av, len); return(r);}static inline intputstat(struct stat32 *ubuf, struct stat *kbuf){ int err; err = put_user (kbuf->st_dev, &ubuf->st_dev); err |= __put_user (kbuf->st_ino, &ubuf->st_ino); err |= __put_user (kbuf->st_mode, &ubuf->st_mode); err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink); err |= __put_user (kbuf->st_uid, &ubuf->st_uid); err |= __put_user (kbuf->st_gid, &ubuf->st_gid); err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev); err |= __put_user (kbuf->st_size, &ubuf->st_size); err |= __put_user (kbuf->st_atime, &ubuf->st_atime); err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime); err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime); err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize); err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks); return err;}extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf);asmlinkage longsys32_newstat(char * filename, struct stat32 *statbuf){ int ret; struct stat s; mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_newstat(filename, &s); set_fs (old_fs); if (putstat (statbuf, &s)) return -EFAULT; return ret;}extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf);asmlinkage longsys32_newlstat(char * filename, struct stat32 *statbuf){ int ret; struct stat s; mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_newlstat(filename, &s); set_fs (old_fs); if (putstat (statbuf, &s)) return -EFAULT; return ret;}extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf);asmlinkage longsys32_newfstat(unsigned int fd, struct stat32 *statbuf){ int ret; struct stat s; mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_newfstat(fd, &s); set_fs (old_fs); if (putstat (statbuf, &s)) return -EFAULT; return ret;}#define ALIGN4K(a) (((a) + 0xfff) & ~0xfff)#define OFFSET4K(a) ((a) & 0xfff)unsigned longdo_mmap_fake(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, loff_t off){ struct inode *inode; void *front, *back; unsigned long baddr; int r; char c; if (OFFSET4K(addr) || OFFSET4K(off)) return -EINVAL; prot |= PROT_WRITE; front = NULL; back = NULL; if ((baddr = (addr & PAGE_MASK)) != addr && get_user(c, (char *)baddr) == 0) { front = kmalloc(addr - baddr, GFP_KERNEL); __copy_user(front, (void *)baddr, addr - baddr); } if (addr && ((addr + len) & ~PAGE_MASK) && get_user(c, (char *)(addr + len)) == 0) { back = kmalloc(PAGE_SIZE - ((addr + len) & ~PAGE_MASK), GFP_KERNEL); __copy_user(back, (char *)addr + len, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); } down(¤t->mm->mmap_sem); r = do_mmap(0, baddr, len + (addr - baddr), prot, flags | MAP_ANONYMOUS, 0); up(¤t->mm->mmap_sem); if (r < 0) return(r); if (addr == 0) addr = r; if (back) { __copy_user((char *)addr + len, back, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); kfree(back); } if (front) { __copy_user((void *)baddr, front, addr - baddr); kfree(front); } if (flags & MAP_ANONYMOUS) { clear_user((char *)addr, len); return(addr); } if (!file) return -EINVAL; inode = file->f_dentry->d_inode; if (!inode->i_fop) return -EINVAL; if (!file->f_op->read) return -EINVAL; r = file->f_op->read(file, (char *)addr, len, &off); return (r < 0) ? -EINVAL : addr;}longia32_do_mmap (struct file *file, unsigned int addr, unsigned int len, unsigned int prot, unsigned int flags, unsigned int fd, unsigned int offset){ long error = -EFAULT; unsigned int poff; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); prot |= PROT_EXEC; if ((flags & MAP_FIXED) && ((addr & ~PAGE_MASK) || (offset & ~PAGE_MASK))) error = do_mmap_fake(file, addr, len, prot, flags, (loff_t)offset); else { poff = offset & PAGE_MASK; len += offset - poff; down(¤t->mm->mmap_sem); error = do_mmap_pgoff(file, addr, len, prot, flags, poff >> PAGE_SHIFT); up(¤t->mm->mmap_sem); if (!IS_ERR((void *) error)) error += offset - poff; } return error;}/* * Linux/i386 didn't use to be able to handle more than * 4 system call parameters, so these system calls used a memory * block for parameter passing.. */struct mmap_arg_struct { unsigned int addr; unsigned int len; unsigned int prot; unsigned int flags; unsigned int fd; unsigned int offset;};asmlinkage longsys32_mmap(struct mmap_arg_struct *arg){ struct mmap_arg_struct a; struct file *file = NULL; long retval; if (copy_from_user(&a, arg, sizeof(a))) return -EFAULT; if (!(a.flags & MAP_ANONYMOUS)) { file = fget(a.fd); if (!file) return -EBADF; } retval = ia32_do_mmap(file, a.addr, a.len, a.prot, a.flags, a.fd, a.offset); if (file) fput(file); return retval;}asmlinkage longsys32_pipe(int *fd){ int retval; int fds[2]; retval = do_pipe(fds); if (retval) goto out; if (copy_to_user(fd, fds, sizeof(fds))) retval = -EFAULT; out: return retval;}asmlinkage longsys32_mprotect(unsigned long start, size_t len, unsigned long prot){ if (prot == 0) return(0); len += start & ~PAGE_MASK; if ((start & ~PAGE_MASK) && (prot & PROT_WRITE)) prot |= PROT_EXEC; return(sys_mprotect(start & PAGE_MASK, len & PAGE_MASK, prot));}asmlinkage longsys32_rt_sigaction(int sig, struct sigaction32 *act, struct sigaction32 *oact, unsigned int sigsetsize){ struct k_sigaction new_ka, old_ka; int ret; sigset32_t set32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(sigset32_t)) return -EINVAL; if (act) { ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); ret |= __copy_from_user(&set32, &act->sa_mask, sizeof(sigset32_t)); switch (_NSIG_WORDS) { case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] | (((long)set32.sig[7]) << 32); case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] | (((long)set32.sig[5]) << 32); case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] | (((long)set32.sig[3]) << 32); case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] | (((long)set32.sig[1]) << 32); } ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); if (ret) return -EFAULT; } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { switch (_NSIG_WORDS) { case 4: set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); set32.sig[6] = old_ka.sa.sa_mask.sig[3]; case 3: set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); set32.sig[4] = old_ka.sa.sa_mask.sig[2]; case 2: set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); set32.sig[2] = old_ka.sa.sa_mask.sig[1]; case 1: set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); set32.sig[0] = old_ka.sa.sa_mask.sig[0]; } ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); ret |= __copy_to_user(&oact->sa_mask, &set32, sizeof(sigset32_t)); ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); } return ret;}extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize);asmlinkage longsys32_rt_sigprocmask(int how, sigset32_t *set, sigset32_t *oset, unsigned int sigsetsize){ sigset_t s; sigset32_t s32; int ret; mm_segment_t old_fs = get_fs(); if (set) { if (copy_from_user (&s32, set, sizeof(sigset32_t))) return -EFAULT; switch (_NSIG_WORDS) { case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); } } set_fs (KERNEL_DS); ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, sigsetsize); set_fs (old_fs); if (ret) return ret; if (oset) { switch (_NSIG_WORDS) { case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; } if (copy_to_user (oset, &s32, sizeof(sigset32_t))) return -EFAULT; } return 0;}static inline intput_statfs (struct statfs32 *ubuf, struct statfs *kbuf){ int err; err = put_user (kbuf->f_type, &ubuf->f_type); err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); err |= __put_user (kbuf->f_files, &ubuf->f_files); err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); return err;}extern asmlinkage long sys_statfs(const char * path, struct statfs * buf);asmlinkage longsys32_statfs(const char * path, struct statfs32 *buf){ int ret; struct statfs s; mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_statfs((const char *)path, &s); set_fs (old_fs); if (put_statfs(buf, &s)) return -EFAULT; return ret;}extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf);asmlinkage longsys32_fstatfs(unsigned int fd, struct statfs32 *buf){ int ret; struct statfs s; mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_fstatfs(fd, &s); set_fs (old_fs); if (put_statfs(buf, &s)) return -EFAULT; return ret;}struct timeval32{ int tv_sec, tv_usec;};struct itimerval32{ struct timeval32 it_interval; struct timeval32 it_value;};static inline longget_tv32(struct timeval *o, struct timeval32 *i){ return (!access_ok(VERIFY_READ, i, sizeof(*i)) || (__get_user(o->tv_sec, &i->tv_sec) | __get_user(o->tv_usec, &i->tv_usec))); return ENOSYS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -