diff options
Diffstat (limited to 'kernel/api/binfmt.c')
-rw-r--r-- | kernel/api/binfmt.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/kernel/api/binfmt.c b/kernel/api/binfmt.c new file mode 100644 index 0000000..1597fdf --- /dev/null +++ b/kernel/api/binfmt.c @@ -0,0 +1,88 @@ +#include "errno.h" + +#include "main/inits.h" + +#include "fs/fcntl.h" +#include "fs/file.h" +#include "fs/vfs_syscall.h" + +#include "util/debug.h" +#include "util/init.h" +#include "util/list.h" + +#include "mm/kmalloc.h" + +#include "api/binfmt.h" + +typedef struct binfmt +{ + const char *bf_id; + binfmt_load_func_t bf_load; + list_link_t bf_link; +} binfmt_t; + +static list_t binfmt_list = LIST_INITIALIZER(binfmt_list); + +long binfmt_add(const char *id, binfmt_load_func_t loadfunc) +{ + binfmt_t *fmt; + if (NULL == (fmt = kmalloc(sizeof(*fmt)))) + { + return -ENOMEM; + } + + dbg(DBG_EXEC, "Registering binary loader %s\n", id); + + fmt->bf_id = id; + fmt->bf_load = loadfunc; + list_insert_head(&binfmt_list, &fmt->bf_link); + + return 0; +} + +long binfmt_load(const char *filename, char *const *argv, char *const *envp, + uint64_t *rip, uint64_t *rsp) +{ + long fd = do_open(filename, O_RDONLY); + if (fd < 0) + { + dbg(DBG_EXEC, "ERROR: exec failed to open file %s\n", filename); + return fd; + } + file_t *file = fget((int)fd); + long ret = 0; + if (S_ISDIR(file->f_vnode->vn_mode)) + { + ret = -EISDIR; + } + if (!ret && !S_ISREG(file->f_vnode->vn_mode)) + { + ret = -EACCES; + } + fput(&file); + if (ret) + { + do_close((int)fd); + return ret; + } + + list_iterate(&binfmt_list, fmt, binfmt_t, bf_link) + { + dbg(DBG_EXEC, "Trying to exec %s using binary loader %s\n", filename, + fmt->bf_id); + + /* ENOEXE indicates that the given loader is unable to load + * the given file, any other error indicates that the file + * was recognized, but some other error existed which should + * be returned to the user, only if all loaders specify ENOEXEC + * do we actually return ENOEXEC */ + ret = fmt->bf_load(filename, (int)fd, argv, envp, rip, rsp); + if (ret != -ENOEXEC) + { + do_close((int)fd); + } + } + + do_close((int)fd); + return ret; +} |