695 lines
17 KiB

/*
* 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 <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <a.out.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/segment.h>
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() {}