linux32.c
来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 2,612 行 · 第 1/5 页
C
2,612 行
/* * Conversion between 32-bit and 64-bit native system calls. * * Copyright (C) 2000 Silicon Graphics, Inc. * Written by Ulf Carlsson (ulfc@engr.sgi.com) * sys32_execve from ia64/ia32 code, Feb 2000, Kanoj Sarcar (kanoj@sgi.com) */#include <linux/config.h>#include <linux/mm.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/smp_lock.h>#include <linux/highuid.h>#include <linux/dirent.h>#include <linux/resource.h>#include <linux/highmem.h>#include <linux/time.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/filter.h>#include <linux/shm.h>#include <linux/sem.h>#include <linux/msg.h>#include <linux/icmpv6.h>#include <linux/sysctl.h>#include <linux/utime.h>#include <linux/utsname.h>#include <linux/personality.h>#include <linux/timex.h>#include <linux/dnotify.h>#include <linux/linkage.h>#include <linux/module.h>#include <net/sock.h>#include <net/scm.h>#include <asm/uaccess.h>#include <asm/mman.h>#include <asm/ipc.h>extern asmlinkage long sys_socket(int family, int type, int protocol);extern asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);extern asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, int addrlen);extern asmlinkage long sys_listen(int fd, int backlog);extern asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen);extern asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len);extern asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len);extern asmlinkage long sys_socketpair(int family, int type, int protocol, int *usockvec);extern asmlinkage long sys_send(int fd, void * buff, size_t len, unsigned flags);extern asmlinkage long sys_sendto(int fd, void * buff, size_t len, unsigned flags, struct sockaddr *addr, int addr_len);extern asmlinkage long sys_recv(int fd, void * ubuf, size_t size, unsigned flags);extern asmlinkage long sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, struct sockaddr *addr, int *addr_len);extern asmlinkage long sys_shutdown(int fd, int how);extern asmlinkage long sys_setsockopt(int fd, int level, int optname, char *optval, int optlen);extern asmlinkage long sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen);extern asmlinkage long sys_sendmsg(int fd, struct msghdr *msg, unsigned flags);extern asmlinkage long sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags);/* Use this to get at 32-bit user passed pointers. *//* A() macro should be used for places where you e.g. have some internal variable u32 and just want to get rid of a compiler warning. AA() has to be used in places where you want to convert a function argument to 32bit pointer or when you e.g. access pt_regs structure and want to consider 32bit registers only. */#define A(__x) ((unsigned long)(__x))#define AA(__x) ((unsigned long)((int)__x))#ifdef __MIPSEB__#define merge_64(r1,r2) ((((r1) & 0xffffffffUL) << 32) + ((r2) & 0xffffffffUL))#endif#ifdef __MIPSEL__#define merge_64(r1,r2) ((((r2) & 0xffffffffUL) << 32) + ((r1) & 0xffffffffUL))#endif/* * Revalidate the inode. This is required for proper NFS attribute caching. */static __inline__ intdo_revalidate(struct dentry *dentry){ struct inode * inode = dentry->d_inode; if (inode->i_op && inode->i_op->revalidate) return inode->i_op->revalidate(dentry); return 0;}static int cp_new_stat32(struct inode * inode, struct stat32 * statbuf){ struct stat32 tmp; unsigned int blocks, indirect; memset(&tmp, 0, sizeof(tmp)); tmp.st_dev = kdev_t_to_nr(inode->i_dev); tmp.st_ino = inode->i_ino; tmp.st_mode = inode->i_mode; tmp.st_nlink = inode->i_nlink; SET_STAT_UID(tmp, inode->i_uid); SET_STAT_GID(tmp, inode->i_gid); tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; /* * st_blocks and st_blksize are approximated with a simple algorithm if * they aren't supported directly by the filesystem. The minix and msdos * filesystems don't keep track of blocks, so they would either have to * be counted explicitly (by delving into the file itself), or by using * this simple algorithm to get a reasonable (although not 100% * accurate) value. */ /* * Use minix fs values for the number of direct and indirect blocks. * The count is now exact for the minix fs except that it counts zero * blocks. Everything is in units of BLOCK_SIZE until the assignment * to tmp.st_blksize. */#define D_B 7#define I_B (BLOCK_SIZE / sizeof(unsigned short)) if (!inode->i_blksize) { blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; if (blocks > D_B) { indirect = (blocks - D_B + I_B - 1) / I_B; blocks += indirect; if (indirect > 1) { indirect = (indirect - 1 + I_B - 1) / I_B; blocks += indirect; if (indirect > 1) blocks++; } } tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; tmp.st_blksize = BLOCK_SIZE; } else { tmp.st_blocks = inode->i_blocks; tmp.st_blksize = inode->i_blksize; } return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;}asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf){ struct nameidata nd; int error; error = user_path_walk(filename, &nd); if (!error) { error = do_revalidate(nd.dentry); if (!error) error = cp_new_stat32(nd.dentry->d_inode, statbuf); path_release(&nd); } return error;}asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf){ struct nameidata nd; int error; error = user_path_walk_link(filename, &nd); if (!error) { error = do_revalidate(nd.dentry); if (!error) error = cp_new_stat32(nd.dentry->d_inode, statbuf); path_release(&nd); } return error;}asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 * statbuf){ struct file * f; int err = -EBADF; f = fget(fd); if (f) { struct dentry * dentry = f->f_dentry; err = do_revalidate(dentry); if (!err) err = cp_new_stat32(dentry->d_inode, statbuf); fput(f); } return err;}asmlinkage unsigned longsys32_mmap2(unsigned long addr, size_t len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff){ struct file * file = NULL; unsigned long error; error = -EINVAL; if (!(flags & MAP_ANONYMOUS)) { error = -EBADF; file = fget(fd); if (!file) goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); up_write(¤t->mm->mmap_sem); if (file) fput(file);out: return error;}asmlinkage long sys_truncate(const char * path, unsigned long length);asmlinkage int sys_truncate64(const char *path, unsigned int high, unsigned int low){ if ((int)high < 0) return -EINVAL; return sys_truncate(path, ((long) high << 32) | low);}asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length);asmlinkage int sys_ftruncate64(unsigned int fd, unsigned int high, unsigned int low){ if ((int)high < 0) return -EINVAL; return sys_ftruncate(fd, ((long) high << 32) | low);}extern asmlinkage int sys_utime(char * filename, struct utimbuf * times);struct utimbuf32 { __kernel_time_t32 actime, modtime;};asmlinkage int sys32_utime(char * filename, struct utimbuf32 *times){ struct utimbuf t; mm_segment_t old_fs; int ret; char *filenam; if (!times) return sys_utime(filename, NULL); if (get_user (t.actime, ×->actime) || __get_user (t.modtime, ×->modtime)) return -EFAULT; filenam = getname (filename); ret = PTR_ERR(filenam); if (!IS_ERR(filenam)) { old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_utime(filenam, &t); set_fs (old_fs); putname (filenam); } return ret;}#if 0/* * count32() counts the number of arguments/envelopes */static int count32(u32 * argv, int max){ int i = 0; if (argv != NULL) { for (;;) { u32 p; int error; error = get_user(p,argv); if (error) return error; if (!p) break; argv++; if (++i > max) return -E2BIG; } } return i;}/* * 'copy_strings32()' 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. */int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm){ while (argc-- > 0) { u32 str; int len; unsigned long pos; if (get_user(str, argv+argc) || !str || !(len = strnlen_user((char *)A(str), bprm->p))) return -EFAULT; if (bprm->p < len) return -E2BIG; bprm->p -= len; /* XXX: add architecture specific overflow check here. */ pos = bprm->p; while (len > 0) { char *kaddr; int i, new, err; struct page *page; int offset, bytes_to_copy; offset = pos % PAGE_SIZE; i = pos/PAGE_SIZE; page = bprm->page[i]; new = 0; if (!page) { page = alloc_page(GFP_HIGHUSER); bprm->page[i] = page; if (!page) return -ENOMEM; new = 1; } kaddr = kmap(page); if (new && offset) memset(kaddr, 0, offset); bytes_to_copy = PAGE_SIZE - offset; if (bytes_to_copy > len) { bytes_to_copy = len; if (new) memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len); } err = copy_from_user(kaddr + offset, (char *)A(str), bytes_to_copy); kunmap(page); if (err) return -EFAULT; pos += bytes_to_copy; str += bytes_to_copy; len -= bytes_to_copy; } } return 0;}/* * sys_execve32() executes a new program. */int do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs){ struct linux_binprm bprm; struct dentry * dentry; int retval; int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); dentry = open_namei(filename, 0, 0); retval = PTR_ERR(dentry); if (IS_ERR(dentry)) return retval; bprm.dentry = dentry; bprm.filename = filename; bprm.sh_bang = 0; bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count32(argv, bprm.p / sizeof(u32))) < 0) { dput(dentry); return bprm.argc; } if ((bprm.envc = count32(envp, bprm.p / sizeof(u32))) < 0) { dput(dentry); return bprm.envc; } retval = prepare_binprm(&bprm); if (retval < 0) goto out; retval = copy_strings_kernel(1, &bprm.filename, &bprm); if (retval < 0) goto out; bprm.exec = bprm.p; retval = copy_strings32(bprm.envc, envp, &bprm); if (retval < 0) goto out; retval = copy_strings32(bprm.argc, argv, &bprm); if (retval < 0) goto out; retval = search_binary_handler(&bprm,regs); if (retval >= 0) /* execve success */ return retval;out: /* Something went wrong, return the inode and free the argument pages*/ if (bprm.dentry) dput(bprm.dentry); /* Assumes that free_page() can take a NULL argument. */ /* I hope this is ok for all architectures */ for (i = 0 ; i < MAX_ARG_PAGES ; i++) if (bprm.page[i]) __free_page(bprm.page[i]); return retval;}/* * sys_execve() executes a new program. */asmlinkage int sys32_execve(abi64_no_regargs, struct pt_regs regs){ int error; char * filename; filename = getname((char *) (long)regs.regs[4]); printk("Executing: %s\n", filename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; error = do_execve32(filename, (u32 *) (long)regs.regs[5], (u32 *) (long)regs.regs[6], ®s); putname(filename);out: return error;}#elsestatic int nargs(unsigned int arg, char **ap){ unsigned int addr; int n, err; if (!arg) return 0; n = 0; do { err = get_user(addr, (unsigned int *)A(arg)); if (err) return err; if (ap) *ap++ = (char *) A(addr); arg += sizeof(unsigned int); n++; if (n >= (MAX_ARG_PAGES * PAGE_SIZE) / sizeof(char *)) return -E2BIG; } while (addr); return n - 1;}asmlinkage intsys32_execve(abi64_no_regargs, struct pt_regs regs){ extern asmlinkage int sys_execve(abi64_no_regargs, struct pt_regs regs); extern asmlinkage long sys_munmap(unsigned long addr, size_t len); unsigned int argv = (unsigned int)regs.regs[5]; unsigned int envp = (unsigned int)regs.regs[6]; char **av, **ae; int na, ne, r, len; char * filename; 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' fails. */ down_write(¤t->mm->mmap_sem);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?