/* * linux/fs/exec.c * * (C) 1991 Linus Torvalds */ /* * #!-checking implemented by tytso. */ /* * Demand-loading implemented 01.12.91 - no need to read anything but * the header into memory. The inode of the executable is put into * "current->executable", and page faults do the actual loading. Clean. * * Once more I can proudly say that linux stood up to being changed: it * was less than 2 hours work to get demand-loading completely implemented. */ #include #include #include #include #include #include #include #include #include extern int sys_exit(int exit_code); extern int sys_close(int fd); /* * MAX_ARG_PAGES defines the number of pages allocated for arguments * and envelope for the new program. 32 should suffice, this gives * a maximum env+arg of 128kB ! */ #define MAX_ARG_PAGES 32 int sys_uselib() { return -ENOSYS; } /* * create_tables() parses the env- and arg-strings in new user * memory and creates the pointer tables from them, and puts their * addresses on the "stack", returning the new stack pointer value. */ static unsigned long *create_tables(char *p, int argc, int envc) { unsigned long *argv, *envp; unsigned long *sp; sp = (unsigned long *)(0xfffffffc & (unsigned long)p); sp -= envc + 1; envp = sp; sp -= argc + 1; argv = sp; put_fs_long((unsigned long)envp, --sp); put_fs_long((unsigned long)argv, --sp); put_fs_long((unsigned long)argc, --sp); while (argc-- > 0) { put_fs_long((unsigned long)p, argv++); while (get_fs_byte(p++)) /* nothing */ ; } put_fs_long(0, argv); while (envc-- > 0) { put_fs_long((unsigned long)p, envp++); while (get_fs_byte(p++)) /* nothing */ ; } put_fs_long(0, envp); return sp; } /* * count() counts the number of arguments/envelopes */ static int count(char **argv) { int i = 0; char **tmp; if (tmp = argv) while (get_fs_long((unsigned long *)(tmp++))) i++; return i; } /* * 'copy_string()' 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. * * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies * whether the string and the string array are from user or kernel segments: * * from_kmem argv * argv ** * 0 user space user space * 1 kernel space user space * 2 kernel space kernel space * * We do this by playing games with the fs segment register. Since it * it is expensive to load a segment register, we try to avoid calling * set_fs() unless we absolutely have to. */ static unsigned long copy_strings(int argc, char **argv, unsigned long *page, unsigned long p, int from_kmem) { char *tmp, *pag; int len, offset = 0; unsigned long old_fs, new_fs; if (!p) return 0; /* bullet-proofing */ new_fs = get_ds(); old_fs = get_fs(); if (from_kmem == 2) set_fs(new_fs); while (argc-- > 0) { if (from_kmem == 1) set_fs(new_fs); if (!(tmp = (char *)get_fs_long(((unsigned long *)argv) + argc))) panic("argc is wrong"); if (from_kmem == 1) set_fs(old_fs); len = 0; /* remember zero-padding */ do { len++; } while (get_fs_byte(tmp++)); if (p - len < 0) { /* this shouldn't happen - 128kB */ set_fs(old_fs); return 0; } while (len) { --p; --tmp; --len; if (--offset < 0) { offset = p % PAGE_SIZE; if (from_kmem == 2) set_fs(old_fs); if (!(pag = (char *)page[p / PAGE_SIZE]) && !(pag = (char *)(page[p / PAGE_SIZE] = (unsigned long *)get_free_page()))) return 0; if (from_kmem == 2) set_fs(new_fs); } *(pag + offset) = get_fs_byte(tmp); } } if (from_kmem == 2) set_fs(old_fs); return p; } static unsigned long change_ldt(unsigned long text_size, unsigned long *page) { unsigned long code_limit, data_limit, code_base, data_base; int i; code_limit = text_size + PAGE_SIZE - 1; code_limit &= 0xFFFFF000; data_limit = 0x4000000; code_base = get_base(current->ldt[1]); data_base = code_base; set_base(current->ldt[1], code_base); set_limit(current->ldt[1], code_limit); set_base(current->ldt[2], data_base); set_limit(current->ldt[2], data_limit); /* make sure fs points to the NEW data segment */ __asm__("pushl $0x17\n\tpop %%fs" ::); data_base += data_limit; for (i = MAX_ARG_PAGES - 1; i >= 0; i--) { data_base -= PAGE_SIZE; if (page[i]) put_page(page[i], data_base); } return data_limit; } /* * 'do_execve()' executes a new program. */ int do_execve(unsigned long *eip, long tmp, char *filename, char **argv, char **envp) { struct m_inode *inode; struct buffer_head *bh; struct exec ex; unsigned long page[MAX_ARG_PAGES]; int i, argc, envc; int e_uid, e_gid; int retval; int sh_bang = 0; unsigned long p = PAGE_SIZE * MAX_ARG_PAGES - 4; if ((0xffff & eip[1]) != 0x000f) panic("execve called from supervisor mode"); for (i = 0; i < MAX_ARG_PAGES; i++) /* clear page-table */ page[i] = 0; if (!(inode = namei(filename))) /* get executables inode */ return -ENOENT; argc = count(argv); envc = count(envp); restart_interp: if (!S_ISREG(inode->i_mode)) { /* must be regular file */ retval = -EACCES; goto exec_error2; } i = inode->i_mode; e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; if (current->euid == inode->i_uid) i >>= 6; else if (current->egid == inode->i_gid) i >>= 3; if (!(i & 1) && !((inode->i_mode & 0111) && suser())) { retval = -ENOEXEC; goto exec_error2; } if (!(bh = bread(inode->i_dev, inode->i_zone[0]))) { retval = -EACCES; goto exec_error2; } ex = *((struct exec *)bh->b_data); /* read exec-header */ if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { /* * This section does the #! interpretation. * Sorta complicated, but hopefully it will work. -TYT */ char buf[1023], *cp, *interp, *i_name, *i_arg; unsigned long old_fs; strncpy(buf, bh->b_data + 2, 1022); brelse(bh); iput(inode); buf[1022] = '\0'; if (cp = strchr(buf, '\n')) { *cp = '\0'; for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++) ; } if (!cp || *cp == '\0') { retval = -ENOEXEC; /* No interpreter name found */ goto exec_error1; } interp = i_name = cp; i_arg = 0; for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { if (*cp == '/') i_name = cp + 1; } if (*cp) { *cp++ = '\0'; i_arg = cp; } /* * OK, we've parsed out the interpreter name and * (optional) argument. */ if (sh_bang++ == 0) { p = copy_strings(envc, envp, page, p, 0); p = copy_strings(--argc, argv + 1, page, p, 0); } /* * Splice in (1) the interpreter's name for argv[0] * (2) (optional) argument to interpreter * (3) filename of shell script * * This is done in reverse order, because of how the * user environment and arguments are stored. */ p = copy_strings(1, &filename, page, p, 1); argc++; if (i_arg) { p = copy_strings(1, &i_arg, page, p, 2); argc++; } p = copy_strings(1, &i_name, page, p, 2); argc++; if (!p) { retval = -ENOMEM; goto exec_error1; } /* * OK, now restart the process with the interpreter's inode. */ old_fs = get_fs(); set_fs(get_ds()); if (!(inode = namei(interp))) { /* get executables inode */ set_fs(old_fs); retval = -ENOENT; goto exec_error1; } set_fs(old_fs); goto restart_interp; } brelse(bh); if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || ex.a_text + ex.a_data + ex.a_bss > 0x3000000 || inode->i_size < ex.a_text + ex.a_data + ex.a_syms + N_TXTOFF(ex)) { retval = -ENOEXEC; goto exec_error2; } if (N_TXTOFF(ex) != BLOCK_SIZE) { printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); retval = -ENOEXEC; goto exec_error2; } if (!sh_bang) { p = copy_strings(envc, envp, page, p, 0); p = copy_strings(argc, argv, page, p, 0); if (!p) { retval = -ENOMEM; goto exec_error2; } } /* OK, This is the point of no return */ if (current->executable) iput(current->executable); current->executable = inode; for (i = 0; i < 32; i++) current->sigaction[i].sa_handler = NULL; for (i = 0; i < NR_OPEN; i++) if ((current->close_on_exec >> i) & 1) sys_close(i); current->close_on_exec = 0; free_page_tables(get_base(current->ldt[1]), get_limit(0x0f)); free_page_tables(get_base(current->ldt[2]), get_limit(0x17)); if (last_task_used_math == current) last_task_used_math = NULL; current->used_math = 0; p += change_ldt(ex.a_text, page) - MAX_ARG_PAGES * PAGE_SIZE; p = (unsigned long)create_tables((char *)p, argc, envc); current->brk = ex.a_bss + (current->end_data = ex.a_data + (current->end_code = ex.a_text)); current->start_stack = p & 0xfffff000; current->euid = e_uid; current->egid = e_gid; i = ex.a_text + ex.a_data; while (i & 0xfff) put_fs_byte(0, (char *)(i++)); eip[0] = ex.a_entry; /* eip, magic happens :-) */ eip[3] = p; /* stack pointer */ return 0; exec_error2: iput(inode); exec_error1: for (i = 0; i < MAX_ARG_PAGES; i++) free_page(page[i]); return (retval); } int do_execve2(unsigned long *eip, long tmp, char *filename, char **argv, char **envp) { struct m_inode *inode; struct buffer_head *bh; struct exec ex; unsigned long page[MAX_ARG_PAGES]; int i, argc, envc; int e_uid, e_gid; int retval; int sh_bang = 0; unsigned long p = PAGE_SIZE * MAX_ARG_PAGES - 4; if ((0xffff & eip[1]) != 0x000f) panic("execve called from supervisor mode"); for (i = 0; i < MAX_ARG_PAGES; i++) /* clear page-table */ page[i] = 0; if (!(inode = namei(filename))) /* get executables inode */ return -ENOENT; argc = count(argv); envc = count(envp); restart_interp: if (!S_ISREG(inode->i_mode)) { /* must be regular file */ retval = -EACCES; goto exec_error2; } i = inode->i_mode; e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; if (current->euid == inode->i_uid) i >>= 6; else if (current->egid == inode->i_gid) i >>= 3; if (!(i & 1) && !((inode->i_mode & 0111) && suser())) { retval = -ENOEXEC; goto exec_error2; } if (!(bh = bread(inode->i_dev, inode->i_zone[0]))) { retval = -EACCES; goto exec_error2; } ex = *((struct exec *)bh->b_data); /* read exec-header */ if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { /* * This section does the #! interpretation. * Sorta complicated, but hopefully it will work. -TYT */ char buf[1023], *cp, *interp, *i_name, *i_arg; unsigned long old_fs; strncpy(buf, bh->b_data + 2, 1022); brelse(bh); iput(inode); buf[1022] = '\0'; if (cp = strchr(buf, '\n')) { *cp = '\0'; for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++) ; } if (!cp || *cp == '\0') { retval = -ENOEXEC; /* No interpreter name found */ goto exec_error1; } interp = i_name = cp; i_arg = 0; for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { if (*cp == '/') i_name = cp + 1; } if (*cp) { *cp++ = '\0'; i_arg = cp; } /* * OK, we've parsed out the interpreter name and * (optional) argument. */ if (sh_bang++ == 0) { p = copy_strings(envc, envp, page, p, 0); p = copy_strings(--argc, argv + 1, page, p, 0); } /* * Splice in (1) the interpreter's name for argv[0] * (2) (optional) argument to interpreter * (3) filename of shell script * * This is done in reverse order, because of how the * user environment and arguments are stored. */ p = copy_strings(1, &filename, page, p, 1); argc++; if (i_arg) { p = copy_strings(1, &i_arg, page, p, 2); argc++; } p = copy_strings(1, &i_name, page, p, 2); argc++; if (!p) { retval = -ENOMEM; goto exec_error1; } /* * OK, now restart the process with the interpreter's inode. */ old_fs = get_fs(); set_fs(get_ds()); if (!(inode = namei(interp))) { /* get executables inode */ set_fs(old_fs); retval = -ENOENT; goto exec_error1; } set_fs(old_fs); goto restart_interp; } brelse(bh); if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || ex.a_text + ex.a_data + ex.a_bss > 0x3000000 || inode->i_size < ex.a_text + ex.a_data + ex.a_syms + N_TXTOFF(ex)) { retval = -ENOEXEC; goto exec_error2; } if (N_TXTOFF(ex) != BLOCK_SIZE) { printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); retval = -ENOEXEC; goto exec_error2; } if (!sh_bang) { p = copy_strings(envc, envp, page, p, 0); p = copy_strings(argc, argv, page, p, 0); if (!p) { retval = -ENOMEM; goto exec_error2; } } /* OK, This is the point of no return */ if (current->executable) iput(current->executable); current->executable = inode; for (i = 0; i < 32; i++) current->sigaction[i].sa_handler = NULL; for (i = 0; i < NR_OPEN; i++) if ((current->close_on_exec >> i) & 1) sys_close(i); current->close_on_exec = 0; free_page_tables(get_base(current->ldt[1]), get_limit(0x0f)); free_page_tables(get_base(current->ldt[2]), get_limit(0x17)); if (last_task_used_math == current) last_task_used_math = NULL; current->used_math = 0; p += change_ldt(ex.a_text, page) - MAX_ARG_PAGES * PAGE_SIZE; p = (unsigned long)create_tables((char *)p, argc, envc); current->brk = ex.a_bss + (current->end_data = ex.a_data + (current->end_code = ex.a_text)); current->start_stack = p & 0xfffff000; current->euid = e_uid; current->egid = e_gid; i = ex.a_text + ex.a_data; while (i & 0xfff) put_fs_byte(0, (char *)(i++)); eip[0] = ex.a_entry; /* eip, magic happens :-) */ eip[3] = p; /* stack pointer */ char *tmp_buf; for (tmp_buf = 0; tmp_buf < current->brk; tmp_buf += 4096) do_no_page2(4, current->start_code + tmp_buf); return 0; exec_error2: iput(inode); exec_error1: for (i = 0; i < MAX_ARG_PAGES; i++) free_page(page[i]); return (retval); } int sys_sleep(unsigned int seconds) { sys_signal(14, SIG_IGN, NULL); sys_alarm(seconds); sys_pause(); return 0; } struct linux_dirent { long d_ino; off_t d_off; unsigned short d_reclen; char d_name[14]; }; int sys_getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) { struct m_inode *my_inode = current->filp[fd]->f_inode; struct buffer_head *my_head = bread(my_inode->i_dev, my_inode->i_zone[0]); struct dir_entry *my_dir = (struct dir_entry *)my_head->b_data; unsigned int qq = sizeof(struct linux_dirent); // struct dir_entry *ptr = mydir; int c = 0; int i = 0; struct linux_dirent tmp; while (c * qq < count && my_dir->inode != 0) { tmp.d_ino = my_dir->inode; tmp.d_off = qq; tmp.d_reclen = qq; // strcpy(tmp->d_name, my_dir->name); for (i = 0; i < 14; i++) { tmp.d_name[i] = my_dir->name[i]; } // printk("%d %d %d %s\n", tmp->d_ino, tmp->d_off, tmp->d_reclen, tmp->d_name); for (i = 0; i < qq; i++) { put_fs_byte(((char *)&tmp)[i], ((char *)(dirp + c)) + i); } my_dir += 1; c++; } // dirp->d_name[0] ='o'; // if(my_dir->inode==0&& c*qq!=count) return 0; return c * qq; } long sys_getcwd(char *buf, size_t size) { struct m_inode *kp_inode = current->pwd; struct buffer_head *kp_head = bread(kp_inode->i_dev, kp_inode->i_zone[0]); struct dir_entry *kp_dir = (struct dir_entry *)kp_head->b_data; struct m_inode *cur_inode = kp_inode; struct buffer_head *cur_head = kp_head; struct dir_entry *cur_dir = kp_dir; struct m_inode *father_inode = cur_inode; struct buffer_head *father_head = cur_head; struct dir_entry *father_dir = cur_dir + 1; int c = 0; int i = 0; int k = 0; int j = 0; char path[20][30]; while (1) { father_inode = iget(cur_inode->i_dev, father_dir->inode); father_head = bread(father_inode->i_dev, father_inode->i_zone[0]); father_dir = (struct dir_entry *)father_head->b_data; while (father_dir->inode != cur_dir->inode) { father_dir++; } for (i = 0; father_dir->name[i] != '\0'; i++) { path[c][i] = father_dir->name[i]; } path[c++][i] = '\0'; cur_inode = father_inode; cur_head = father_head; cur_dir = (struct dir_entry *)cur_head->b_data; father_dir = cur_dir + 1; if (father_dir->inode == cur_dir->inode) break; } for (i = c - 1; i >= 0; i--) { put_fs_byte('/', buf + k); k++; for (j = 0; path[i][j] != '\0'; j++) { put_fs_byte(path[i][j], buf + k); k++; } } return buf; } long sys_mmap(void *start, size_t len, int prot, int flags, int fd, off_t off) { struct m_inode *my_inode = current->filp[fd]->f_inode; struct buffer_head *my_head = bread(my_inode->i_dev, my_inode->i_zone[0]); struct dir_entry *my_dir = (struct dir_entry *)my_head->b_data; int i=0; return 0; } int sys_munmap() {} int sys_clone() {}