mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add #! support to execve(2).
This commit is contained in:
parent
3577cb81fe
commit
f8c5adf20f
3 changed files with 222 additions and 0 deletions
|
@ -25,6 +25,7 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <msr.h>
|
#include <msr.h>
|
||||||
|
@ -1035,6 +1036,100 @@ int Process::Execute(const char* programname, const uint8_t* program,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
const char* shebang_lookup_environment(const char* name, char* const* envp)
|
||||||
|
{
|
||||||
|
size_t equalpos = strcspn(name, "=");
|
||||||
|
if ( name[equalpos] == '=' )
|
||||||
|
return NULL;
|
||||||
|
size_t namelen = equalpos;
|
||||||
|
for ( size_t i = 0; envp[i]; i++ )
|
||||||
|
{
|
||||||
|
if ( strncmp(name, envp[i], namelen) )
|
||||||
|
continue;
|
||||||
|
if ( envp[i][namelen] != '=' )
|
||||||
|
continue;
|
||||||
|
return envp[i] + namelen + 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* shebang_tokenize(char** saved)
|
||||||
|
{
|
||||||
|
char* data = *saved;
|
||||||
|
if ( !data )
|
||||||
|
return *saved = NULL;
|
||||||
|
while ( data[0] && isspace(data[0]) )
|
||||||
|
data++;
|
||||||
|
if ( !data[0] )
|
||||||
|
return *saved = NULL;
|
||||||
|
size_t input = 0;
|
||||||
|
size_t output = 0;
|
||||||
|
bool singly = false;
|
||||||
|
bool doubly = false;
|
||||||
|
bool escaped = false;
|
||||||
|
for ( ; data[input]; input++ )
|
||||||
|
{
|
||||||
|
char c = data[input];
|
||||||
|
if ( !escaped && !singly && !doubly && isspace(c) )
|
||||||
|
break;
|
||||||
|
if ( !escaped && !doubly && c == '\'' )
|
||||||
|
{
|
||||||
|
singly = !singly;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !escaped && !singly && c == '"' )
|
||||||
|
{
|
||||||
|
doubly = !doubly;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !singly && !escaped && c == '\\' )
|
||||||
|
{
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( escaped )
|
||||||
|
{
|
||||||
|
switch ( c )
|
||||||
|
{
|
||||||
|
case 'a': c = '\a'; break;
|
||||||
|
case 'b': c = '\b'; break;
|
||||||
|
case 'e': c = '\e'; break;
|
||||||
|
case 'f': c = '\f'; break;
|
||||||
|
case 'n': c = '\n'; break;
|
||||||
|
case 'r': c = '\r'; break;
|
||||||
|
case 't': c = '\t'; break;
|
||||||
|
case 'v': c = '\v'; break;
|
||||||
|
default: break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
data[output++] = c;
|
||||||
|
}
|
||||||
|
if ( data[input] )
|
||||||
|
*saved = data + input + 1;
|
||||||
|
else
|
||||||
|
*saved = NULL;
|
||||||
|
data[output] = '\0';
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t shebang_count_arguments(char* line)
|
||||||
|
{
|
||||||
|
size_t result = 0;
|
||||||
|
while ( shebang_tokenize(&line) )
|
||||||
|
result++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
|
||||||
|
// can be shared somehow, you need to keep this comment in sync as well
|
||||||
|
// as the logic in these files:
|
||||||
|
// * kernel/process.cpp
|
||||||
|
// * libc/unistd/execvpe.cpp
|
||||||
|
// * utils/which.cpp
|
||||||
|
// NOTO: See comments in execvpe() for algorithmic commentary.
|
||||||
|
|
||||||
static
|
static
|
||||||
int sys_execve_kernel(const char* filename,
|
int sys_execve_kernel(const char* filename,
|
||||||
int argc,
|
int argc,
|
||||||
|
@ -1077,8 +1172,133 @@ int sys_execve_kernel(const char* filename,
|
||||||
|
|
||||||
int result = process->Execute(filename, buffer, filesize, argc, argv, envc, envp, regs);
|
int result = process->Execute(filename, buffer, filesize, argc, argv, envc, envp, regs);
|
||||||
|
|
||||||
|
if ( result == 0 || errno != ENOEXEC ||
|
||||||
|
filesize < 2 || buffer[0] != '#' || buffer[1] != '!' )
|
||||||
|
return delete[] buffer, result;
|
||||||
|
|
||||||
|
size_t line_length = 0;
|
||||||
|
while ( line_length < filesize && buffer[2 + line_length] != '\n' )
|
||||||
|
line_length++;
|
||||||
|
if ( line_length == filesize )
|
||||||
|
return delete[] buffer, errno = ENOEXEC, -1;
|
||||||
|
|
||||||
|
char* line = new char[line_length+1];
|
||||||
|
if ( !line )
|
||||||
|
return delete[] buffer, -1;
|
||||||
|
memcpy(line, buffer + 2, line_length);
|
||||||
|
line[line_length] = '\0';
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
|
||||||
|
char* line_clone = String::Clone(line);
|
||||||
|
if ( !line_clone )
|
||||||
|
return delete[] line, -1;
|
||||||
|
size_t argument_count = shebang_count_arguments(line_clone);
|
||||||
|
delete[] line_clone;
|
||||||
|
|
||||||
|
if ( !argument_count || INT_MAX < argument_count )
|
||||||
|
return delete[] line, errno = ENOEXEC, -1;
|
||||||
|
|
||||||
|
int sb_argc = (int) argument_count;
|
||||||
|
char** sb_argv = new char*[sb_argc];
|
||||||
|
if ( !sb_argv )
|
||||||
|
return delete[] line, -1;
|
||||||
|
|
||||||
|
char* sb_saved = line;
|
||||||
|
for ( int i = 0; i < sb_argc; i++ )
|
||||||
|
sb_argv[i] = shebang_tokenize(&sb_saved);
|
||||||
|
|
||||||
|
if ( INT_MAX - argc <= sb_argc )
|
||||||
|
return delete[] sb_argv, delete[] line, errno = EOVERFLOW, -1;
|
||||||
|
|
||||||
|
if ( !sb_argv[0][0] )
|
||||||
|
return delete[] sb_argv, delete[] line, errno = ENOENT, -1;
|
||||||
|
|
||||||
|
int new_argc = sb_argc + argc;
|
||||||
|
char** new_argv = new char*[new_argc + 1];
|
||||||
|
if ( !new_argv )
|
||||||
|
return delete[] sb_argv, delete[] line, -1;
|
||||||
|
|
||||||
|
for ( int i = 0; i < sb_argc; i++ )
|
||||||
|
new_argv[i] = sb_argv[i];
|
||||||
|
new_argv[sb_argc + 0] = (char*) filename;
|
||||||
|
for ( int i = 1; i < argc; i++ )
|
||||||
|
new_argv[sb_argc + i] = argv[i];
|
||||||
|
new_argv[new_argc] = (char*) NULL;
|
||||||
|
|
||||||
|
result = -1;
|
||||||
|
|
||||||
|
// (See the above comment block before editing this searching logic)
|
||||||
|
const char* path = shebang_lookup_environment("PATH", envp);
|
||||||
|
bool search_path = !strchr(sb_argv[0], '/') && path;
|
||||||
|
bool any_tries = false;
|
||||||
|
bool any_eacces = false;
|
||||||
|
|
||||||
|
const char* new_argv0 = sb_argv[0];
|
||||||
|
while ( search_path && *path )
|
||||||
|
{
|
||||||
|
size_t len = strcspn(path, ":");
|
||||||
|
if ( !len )
|
||||||
|
{
|
||||||
|
path++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
any_tries = true;
|
||||||
|
|
||||||
|
char* dirpath = strndup(path, len);
|
||||||
|
if ( !dirpath )
|
||||||
|
return -1;
|
||||||
|
if ( (path += len)[0] == ':' )
|
||||||
|
path++;
|
||||||
|
while ( len && dirpath[len - 1] == '/' )
|
||||||
|
dirpath[--len] = '\0';
|
||||||
|
|
||||||
|
char* fullpath;
|
||||||
|
if ( asprintf(&fullpath, "%s/%s", dirpath, sb_argv[0]) < 0 )
|
||||||
|
return free(dirpath), -1;
|
||||||
|
|
||||||
|
result = sys_execve_kernel(fullpath, new_argc, new_argv, envc, envp, regs);
|
||||||
|
|
||||||
|
free(fullpath);
|
||||||
|
free(dirpath);
|
||||||
|
|
||||||
|
if ( result == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ( errno == ENOENT )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( errno == ELOOP ||
|
||||||
|
errno == EISDIR ||
|
||||||
|
errno == ENAMETOOLONG ||
|
||||||
|
errno == ENOTDIR )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( errno == EACCES )
|
||||||
|
{
|
||||||
|
any_eacces = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( errno == EACCES )
|
||||||
|
{
|
||||||
|
any_eacces = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !any_tries )
|
||||||
|
result = sys_execve_kernel(new_argv0, new_argc, new_argv, envc, envp, regs);
|
||||||
|
|
||||||
|
if ( result < 0 && any_eacces )
|
||||||
|
errno = EACCES;
|
||||||
|
|
||||||
|
delete[] new_argv;
|
||||||
|
delete[] sb_argv;
|
||||||
|
delete[] line;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ int execvpe_attempt(const char* filename,
|
||||||
// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
|
// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
|
||||||
// can be shared somehow, you need to keep this comment in sync as well
|
// can be shared somehow, you need to keep this comment in sync as well
|
||||||
// as the logic in these files:
|
// as the logic in these files:
|
||||||
|
// * kernel/process.cpp
|
||||||
// * libc/unistd/execvpe.cpp
|
// * libc/unistd/execvpe.cpp
|
||||||
// * utils/which.cpp
|
// * utils/which.cpp
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
|
// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
|
||||||
// can be shared somehow, you need to keep this comment in sync as well
|
// can be shared somehow, you need to keep this comment in sync as well
|
||||||
// as the logic in these files:
|
// as the logic in these files:
|
||||||
|
// * kernel/process.cpp
|
||||||
// * libc/unistd/execvpe.cpp
|
// * libc/unistd/execvpe.cpp
|
||||||
// * utils/which.cpp
|
// * utils/which.cpp
|
||||||
// NOTO: See comments in execvpe() for algorithmic commentary.
|
// NOTO: See comments in execvpe() for algorithmic commentary.
|
||||||
|
|
Loading…
Reference in a new issue