Compare commits

..

No commits in common. "30e4ef970e2f05065c391fe734aa63ba154eddcf" and "b8ca749a6c41610ebc20b21b917771f4a952c3d0" have entirely different histories.

185 changed files with 789 additions and 5242 deletions

1
.gitignore vendored
View File

@ -6,4 +6,3 @@ env-local.sh
initrd/bin/**
initrd/tests/**
base/
.fakeroot

View File

@ -5,7 +5,7 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
set(CMAKE_CROSSCOMPILING true)
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.2.0)
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.1.0)
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)

View File

@ -1,5 +1,5 @@
# Luna
A very basic POSIX-based operating system for personal computers, written in C++. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
A very basic POSIX operating system for desktop computers, written mostly in C++ and C. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
## Another UNIX clone?
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
@ -8,7 +8,7 @@ A very basic POSIX-based operating system for personal computers, written in C++
- x86_64-compatible lightweight [kernel](kernel/).
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
- [Virtual file system](kernel/src/fs/) with a simple but working [tmpfs](kernel/src/fs/tmpfs/) populated from the initial ramdisk.
- Can [load ELF programs](kernel/src/thread/ELF.cpp) from the file system as userspace tasks.
- Can [load ELF programs](kernel/src/ELF.cpp) from the file system as userspace tasks.
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be mostly POSIX-compatible.
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
- Fully [UTF-8 aware](libluna/include/luna/Utf8.h), **everywhere**.
@ -76,7 +76,7 @@ These images do reflect the latest changes on the `main` branch, but are obvious
## Is there third-party software I can use on Luna?
Not right now, but hopefully we can start porting some software soon!
Not right now, but hopefully we can start porting some software soon! (After the VFS and fork/exec are done, of course. So, in a long time.)
## License
Luna is open-source and free software under the [BSD-2 License](LICENSE).

View File

@ -1,31 +1,27 @@
function(luna_app SOURCE_FILE APP_NAME)
function(luna_app SOURCE_FILE APP_NAME SETUID)
add_executable(${APP_NAME} ${SOURCE_FILE})
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
add_dependencies(${APP_NAME} libc)
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
target_link_libraries(${APP_NAME} PRIVATE os)
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin)
if(${SETUID})
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID)
else()
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin)
endif()
endfunction()
luna_app(init.cpp init)
luna_app(env.cpp env)
luna_app(su.cpp su)
luna_app(sh.cpp sh)
luna_app(cat.cpp cat)
luna_app(date.cpp date)
luna_app(edit.cpp edit)
luna_app(ls.cpp ls)
luna_app(chown.cpp chown)
luna_app(chmod.cpp chmod)
luna_app(mkdir.cpp mkdir)
luna_app(rm.cpp rm)
luna_app(stat.cpp stat)
luna_app(uname.cpp uname)
luna_app(base64.cpp base64)
luna_app(login.cpp login)
luna_app(ipc-test.cpp ipc-test)
luna_app(mount.cpp mount)
luna_app(umount.cpp umount)
luna_app(ps.cpp ps)
luna_app(time.cpp time)
luna_app(ln.cpp ln)
luna_app(init.cpp init OFF)
luna_app(env.cpp env OFF)
luna_app(su.cpp su ON)
luna_app(sh.cpp sh OFF)
luna_app(cat.cpp cat OFF)
luna_app(date.cpp date OFF)
luna_app(edit.cpp edit OFF)
luna_app(ls.cpp ls OFF)
luna_app(chown.cpp chown OFF)
luna_app(chmod.cpp chmod OFF)
luna_app(mkdir.cpp mkdir OFF)
luna_app(rm.cpp rm OFF)
luna_app(stat.cpp stat OFF)
luna_app(uname.cpp uname OFF)

View File

@ -1,39 +0,0 @@
#include <luna/Base64.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
Result<int> luna_main(int argc, char** argv)
{
StringView path;
bool decode { false };
bool allow_garbage { false };
os::ArgumentParser parser;
parser.add_description("Encode or decode Base64 data. If not given a file, reads from standard input.");
parser.add_system_program_info("base64"_sv);
parser.add_positional_argument(path, "file", "-"_sv);
parser.add_switch_argument(decode, 'd', "decode", "decode data");
parser.add_switch_argument(allow_garbage, 'i', "ignore-garbage", "when decoding, ignore non-base64 characters");
parser.parse(argc, argv);
auto file = TRY(os::File::open_input_file(path));
if (!decode)
{
auto data = TRY(file->read_all());
auto encoded = TRY(Base64::encode(data));
os::println("%s", encoded.chars());
}
else
{
auto data = TRY(file->read_all_as_string());
auto decoded = TRY(Base64::decode(data.view(), allow_garbage));
os::File::standard_output()->write(decoded);
}
return 0;
}

View File

@ -6,10 +6,14 @@ using os::File;
static Result<void> do_cat(StringView path)
{
SharedPtr<File> f = TRY(File::open_input_file(path));
SharedPtr<File> f;
auto out = File::standard_output();
if (path == "-") f = File::standard_input();
else
f = TRY(File::open(path, File::ReadOnly));
auto buf = TRY(Buffer::create_sized(4096));
while (1)
{
@ -24,18 +28,15 @@ static Result<void> do_cat(StringView path)
Result<int> luna_main(int argc, char** argv)
{
StringView filename;
Vector<StringView> files;
os::ArgumentParser parser;
parser.add_description("Concatenate files to standard output."_sv);
parser.add_system_program_info("cat"_sv);
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
parser.set_vector_argument(files);
parser.parse(argc, argv);
Vector<StringView> extra_files = TRY(parser.parse(argc, argv));
TRY(do_cat(filename));
for (auto file : files) TRY(do_cat(file));
for (auto file : extra_files) TRY(do_cat(file));
return 0;
}

View File

@ -1,7 +1,7 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
@ -10,7 +10,6 @@ int main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Change the permissions of a file."_sv);
parser.add_system_program_info("chmod"_sv);
parser.add_positional_argument(mode_string, "mode"_sv, true);
parser.add_positional_argument(path, "path"_sv, true);
parser.parse(argc, argv);

View File

@ -1,4 +1,3 @@
#include <grp.h>
#include <luna/String.h>
#include <os/ArgumentParser.h>
#include <pwd.h>
@ -13,7 +12,6 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Change the owner and group of a file."_sv);
parser.add_system_program_info("chown"_sv);
parser.add_positional_argument(user_and_group, "owner"_sv, true);
parser.add_positional_argument(path, "path"_sv, true);
parser.parse(argc, argv);
@ -32,16 +30,8 @@ Result<int> luna_main(int argc, char** argv)
if (rc.has_value()) { gid = (u32)rc.value(); }
else
{
struct group* grp = getgrnam(group.chars());
if (!grp)
{
fprintf(stderr, "%s: unknown group %s!\n", argv[0], group.chars());
return 1;
}
gid = grp->gr_gid;
endgrent();
fprintf(stderr, "FIXME: read entry from group file to determine GID for group %s\n", group.chars());
return 1;
}
}
}
@ -75,8 +65,6 @@ Result<int> luna_main(int argc, char** argv)
gid = pw->pw_gid;
}
endpwent();
}
}

View File

@ -1,16 +1,16 @@
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
Result<int> luna_main(int argc, char** argv)
int main(int argc, char** argv)
{
StringView date;
os::ArgumentParser parser;
parser.add_description("Display the current (or another) date and time."_sv);
parser.add_system_program_info("date"_sv);
parser.add_value_argument(date, 'd', "date"_sv, true,
"the UNIX timestamp to display instead of the current time"_sv);
parser.parse(argc, argv);
@ -19,7 +19,5 @@ Result<int> luna_main(int argc, char** argv)
if (date.is_empty()) { now = time(NULL); }
else { now = strtol(date.chars(), nullptr, 10); }
os::print("%s", ctime(&now));
return 0;
fputs(ctime(&now), stdout);
}

View File

@ -10,7 +10,6 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Edit a file using basic line-based shell editing."_sv);
parser.add_system_program_info("edit"_sv);
parser.add_positional_argument(pathname, "path"_sv, true);
parser.parse(argc, argv);

View File

@ -1,12 +1,12 @@
#include <errno.h>
#include <fcntl.h>
#include <luna/PathParser.h>
#include <luna/Sort.h>
#include <luna/String.h>
#include <luna/Vector.h>
#include <os/Directory.h>
#include <os/File.h>
#include <os/Process.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -18,30 +18,36 @@
FILE* g_init_log;
#define xmknod(path, mode, maj, min) \
if (mknod(path, mode, makedev(maj, min)) < 0) exit(255);
// Too early for console logs (/dev/console is created here!), so we have to resort to exiting with a weird exit code in
// case of failure.
static void populate_devfs()
{
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255);
xmknod("/dev/console", 0666, 1, 0);
xmknod("/dev/null", 0666, 2, 0);
xmknod("/dev/zero", 0666, 2, 1);
xmknod("/dev/fb0", 0222, 3, 0);
}
struct Service
{
String name;
String command;
bool restart { false };
String environment;
String standard_output;
String standard_error;
String standard_input;
bool wait { false };
Option<pid_t> pid {};
};
Vector<Service> g_services;
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
SharedPtr<os::File> input)
static Result<void> service_child(const Service& service)
{
auto args = TRY(service.command.split(" \n"));
if (output) dup2(output->fd(), STDOUT_FILENO);
if (error) dup2(error->fd(), STDERR_FILENO);
if (input) dup2(input->fd(), STDIN_FILENO);
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
else
{
@ -54,32 +60,10 @@ static Result<void> service_child(const Service& service, SharedPtr<os::File> ou
static Result<void> try_start_service(Service& service)
{
SharedPtr<os::File> new_stdout = {};
SharedPtr<os::File> new_stderr = {};
SharedPtr<os::File> new_stdin = {};
if (!service.standard_output.is_empty())
{
new_stdout = TRY(os::File::open_or_create(service.standard_output.view(), os::File::Append, 0600));
new_stdout->set_close_on_exec();
}
if (!service.standard_error.is_empty())
{
new_stderr = TRY(os::File::open_or_create(service.standard_error.view(), os::File::Append, 0600));
new_stderr->set_close_on_exec();
}
if (!service.standard_input.is_empty())
{
new_stdin = TRY(os::File::open(service.standard_input.view(), os::File::ReadOnly));
new_stdin->set_close_on_exec();
}
pid_t pid = TRY(os::Process::fork());
if (pid == 0)
{
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
auto rc = service_child(service);
if (rc.has_error())
{
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
@ -91,18 +75,7 @@ static Result<void> try_start_service(Service& service)
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
if (service.wait)
{
fprintf(g_init_log, "[init] waiting for child process %d to finish\n", pid);
int status;
pid_t id = waitpid(pid, &status, 0);
if (id < 0) { return err(errno); }
fprintf(g_init_log, "[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
}
else
service.pid = pid;
service.pid = pid;
return {};
}
@ -117,9 +90,9 @@ static void start_service(Service& service)
}
}
static Result<void> load_service(const os::Path& path)
static Result<void> load_service(StringView path)
{
fprintf(g_init_log, "[init] reading service file: %s\n", path.name().chars());
fprintf(g_init_log, "[init] reading service file: %s\n", path.chars());
auto file = TRY(os::File::open(path, os::File::ReadOnly));
@ -169,35 +142,6 @@ static Result<void> load_service(const os::Path& path)
continue;
}
if (parts[0].view() == "StandardOutput")
{
service.standard_output = move(parts[1]);
continue;
}
if (parts[0].view() == "StandardError")
{
service.standard_error = move(parts[1]);
continue;
}
if (parts[0].view() == "StandardInput")
{
service.standard_input = move(parts[1]);
continue;
}
if (parts[0].view() == "Wait")
{
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
{
service.wait = true;
continue;
}
service.wait = false;
continue;
}
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
}
@ -222,12 +166,23 @@ static Result<void> load_service(const os::Path& path)
static Result<void> load_services()
{
auto dir = TRY(os::Directory::open("/etc/init"));
DIR* dp = opendir("/etc/init");
if (!dp)
{
fprintf(g_init_log, "[init] cannot open service directory: %s\n", strerror(errno));
return {};
}
auto services = TRY(dir->list(os::Directory::Filter::ParentAndBase));
sort(services.begin(), services.end(), String::compare);
dirent* ent;
while ((ent = readdir(dp)))
{
if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue;
for (const auto& entry : services) TRY(load_service({ dir->fd(), entry.view() }));
auto service_path = TRY(PathParser::join("/etc/init"_sv, ent->d_name));
TRY(load_service(service_path.view()));
}
closedir(dp);
return {};
}
@ -266,14 +221,14 @@ int main()
return 1;
}
populate_devfs();
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
// can print stuff.
stdin = fopen("/dev/console", "r");
stdout = fopen("/dev/console", "w");
stderr = fopen("/dev/console", "w");
umask(022);
g_init_log = fopen("/init.log", "w+");
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);

View File

@ -1,45 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int pfds[2];
if (pipe(pfds) < 0)
{
perror("pipe");
return 1;
}
pid_t child = fork();
if (child == 0)
{
close(pfds[1]);
char buffer[4096];
size_t nread = read(pfds[0], buffer, sizeof(buffer) - 1);
buffer[nread] = 0;
close(pfds[0]);
puts(buffer);
return 0;
}
else if (child == -1)
{
perror("fork");
return 1;
}
close(pfds[0]);
const char* string = "Hello from a child process who just received this message from its parent!";
write(pfds[1], string, strlen(string));
close(pfds[1]);
wait(NULL);
return 0;
}

View File

@ -1,29 +0,0 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv)
{
StringView target;
StringView linkpath;
bool create_symlink { false };
os::ArgumentParser parser;
parser.add_description("Create links between files.");
parser.add_system_program_info("ln"_sv);
parser.add_positional_argument(target, "target", true);
parser.add_positional_argument(linkpath, "linkpath", true);
parser.add_switch_argument(create_symlink, 's', "symbolic"_sv, "create symlinks instead of hard links"_sv);
parser.parse(argc, argv);
auto link_function = create_symlink ? symlink : link;
if (link_function(target.chars(), linkpath.chars()) < 0)
{
perror("ln");
return 1;
}
return 0;
}

View File

@ -1,45 +0,0 @@
#include <luna/String.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <stdio.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv)
{
StringView username;
os::ArgumentParser parser;
parser.add_description("Begin a new session on the system.");
parser.add_system_program_info("login"_sv);
parser.add_positional_argument(username, "user", false);
parser.parse(argc, argv);
if (geteuid() != 0)
{
os::eprintln("error: login must be run as root.");
return 1;
}
putchar('\n');
String name;
if (username.is_empty())
{
auto input = os::File::standard_input();
os::print("Username: ");
name = TRY(input->read_line());
name.trim("\n");
if (name.is_empty()) return 0;
username = name.view();
}
execl("/bin/su", "login", "-lp", "--", username.chars(), nullptr);
perror("su");
return 1;
}

View File

@ -1,114 +1,42 @@
#include <grp.h>
#include <luna/Units.h>
#include <os/ArgumentParser.h>
#include <os/Directory.h>
#include <os/File.h>
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <pwd.h>
void find_user_and_group(struct stat& st, StringView& owner, StringView& group)
{
auto* pw = getpwuid(st.st_uid);
if (!pw) owner = "???";
else
owner = pw->pw_name;
auto* grp = getgrgid(st.st_gid);
if (!grp) group = "???";
else
group = grp->gr_name;
}
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv)
{
StringView pathname;
bool show_all { false };
bool show_almost_all { false };
bool long_list { false };
bool human_readable { false };
bool si { false };
bool follow_symlink_args { false };
bool one_per_line { false };
bool list_directories { false };
os::ArgumentParser parser;
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
parser.add_system_program_info("ls"_sv);
parser.add_positional_argument(pathname, "directory"_sv, "."_sv);
parser.add_switch_argument(show_all, 'a', "all"_sv, "also list hidden files (whose filename begins with a dot)"_sv);
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv);
parser.add_switch_argument(long_list, 'l', ""_sv, "use a long listing format"_sv);
parser.add_switch_argument(human_readable, 'h', "human-readable"_sv,
"with -l, show human-readable sizes e.g. 2KiB, 6GiB"_sv);
parser.add_switch_argument(si, ' ', "si"_sv, "same as -h, but show sizes in powers of 10"_sv);
parser.add_switch_argument(follow_symlink_args, 'H', "dereference-args"_sv,
"follow symbolic links listed as arguments"_sv);
parser.add_switch_argument(one_per_line, '1', ""_sv, "list one file per line"_sv);
parser.add_switch_argument(list_directories, 'd', "directory"_sv, "list directories instead of their contents"_sv);
parser.parse(argc, argv);
Vector<String> files;
int dirfd = AT_FDCWD;
SharedPtr<os::Directory> dir;
if (!long_list) follow_symlink_args = true;
if (os::FileSystem::is_directory(pathname, follow_symlink_args) && !list_directories)
DIR* dp = opendir(pathname.chars());
if (!dp)
{
dir = TRY(os::Directory::open(pathname));
dirfd = dir->fd();
auto filter = os::Directory::Filter::Hidden;
if (show_almost_all) filter = os::Directory::Filter::ParentAndBase;
else if (show_all)
filter = os::Directory::Filter::None;
files = TRY(dir->list(filter));
}
else if (os::FileSystem::exists(pathname, follow_symlink_args))
{
auto str = TRY(String::from_string_view(pathname));
TRY(files.try_append(move(str)));
}
else
return err(ENOENT);
if (!long_list)
{
auto list = TRY(String::join(files, one_per_line ? "\n"_sv : " "_sv));
if (!list.is_empty()) os::println("%s", list.chars());
}
else
{
for (const auto& file : files)
{
struct stat st;
TRY(os::FileSystem::stat({ dirfd, file.view() }, st, false));
auto link = TRY(os::FileSystem::readlink({ dirfd, file.view() }));
StringView owner;
StringView group;
find_user_and_group(st, owner, group);
char formatted_mode[11];
os::format_mode(st.st_mode, formatted_mode);
if (!human_readable && !si)
{
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
st.st_size, file.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
size.chars(), file.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
}
perror("opendir");
return 1;
}
int first_ent = 1;
do {
struct dirent* ent = readdir(dp);
if (!ent) break;
if (show_almost_all && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) continue;
if (!show_all && !show_almost_all && *ent->d_name == '.') continue;
printf(first_ent ? "%s" : " %s", ent->d_name);
first_ent = 0;
} while (1);
putchar('\n');
closedir(dp);
return 0;
}

View File

@ -2,14 +2,6 @@
#include <luna/PathParser.h>
#include <os/ArgumentParser.h>
#include <os/FileSystem.h>
#include <sys/stat.h>
static mode_t s_umask = 0;
static void read_umask()
{
s_umask = umask(0);
}
Result<void> mkdir_recursively(StringView path, mode_t mode)
{
@ -23,7 +15,7 @@ begin:
PathParser parser = TRY(PathParser::create(path.chars()));
auto parent = TRY(parser.dirname());
TRY(mkdir_recursively(parent.view(), (0777 & ~s_umask) | S_IWUSR | S_IXUSR));
TRY(mkdir_recursively(parent.view(), mode));
goto begin;
}
@ -39,20 +31,13 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Create directories."_sv);
parser.add_system_program_info("mkdir"_sv);
parser.add_positional_argument(path, "path"_sv, true);
parser.add_value_argument(mode_string, 'm', "mode"_sv, true, "set the mode for the newly created directory");
parser.add_positional_argument(mode_string, "mode"_sv, "755"_sv);
parser.add_switch_argument(recursive, 'p', "parents"_sv,
"if parent directories do not exist, create them as well"_sv);
parser.parse(argc, argv);
read_umask();
mode_t mode;
if (mode_string.is_empty()) mode = 0777 & ~s_umask;
else
mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8) & 01777;
mode_t mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8);
if (recursive)
{

View File

@ -1,24 +0,0 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <sys/mount.h>
Result<int> luna_main(int argc, char** argv)
{
StringView target;
StringView fstype;
os::ArgumentParser parser;
parser.add_description("Mount a file system.");
parser.add_system_program_info("mount"_sv);
parser.add_positional_argument(target, "mountpoint"_sv, true);
parser.add_value_argument(fstype, 't', "type"_sv, "auto"_sv, "the file system type to use");
parser.parse(argc, argv);
if (mount(target.chars(), fstype.chars()) < 0)
{
perror("mount");
return 1;
}
return 0;
}

View File

@ -1,44 +0,0 @@
#include <errno.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <pwd.h>
#include <sys/pstat.h>
#include <time.h>
Result<int> luna_main(int argc, char** argv)
{
os::ArgumentParser parser;
parser.add_description("Show a list of processes on the system.");
parser.add_system_program_info("ps"_sv);
parser.parse(argc, argv);
os::println("UID PID PPID TIME CMD");
pid_t last = pstat(-1, nullptr);
for (pid_t pid = 1; pid <= last; pid++)
{
static process ps;
if (pstat(pid, &ps) < 0)
{
if (errno == ESRCH) continue;
return err(errno);
}
struct tm tm;
gmtime_r(&ps.ps_time.tv_sec, &tm);
char timebuf[256];
strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
const char* user = "???";
passwd* pw = getpwuid(ps.ps_uid);
if (pw) user = pw->pw_name;
os::println("%-8s %6d %6d %10s %s", user, ps.ps_pid, ps.ps_ppid, timebuf, ps.ps_name);
}
endpwent();
return 0;
}

View File

@ -8,7 +8,6 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Remove a path from the file system."_sv);
parser.add_system_program_info("rm"_sv);
parser.add_positional_argument(path, "path"_sv, true);
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
"remove a directory recursively (by default, rm removes only empty directories)"_sv);

View File

@ -1,10 +1,11 @@
#include <errno.h>
#include <luna/String.h>
#include <luna/Vector.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/FileSystem.h>
#include <os/Process.h>
#include <errno.h>
#include <pwd.h>
#include <stddef.h>
#include <stdio.h>
@ -45,7 +46,6 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("The Luna system's command shell."_sv);
parser.add_system_program_info("sh"_sv);
parser.add_positional_argument(path, "path"_sv, "-"_sv);
parser.add_value_argument(command, 'c', "command"_sv, true, "execute a single command and then exit"_sv);
parser.parse(argc, argv);
@ -70,11 +70,8 @@ Result<int> luna_main(int argc, char** argv)
hostname = g_sysinfo.nodename;
if (getuid() == 0) prompt_end = '#';
struct passwd* pw = getpwuid(getuid());
if (pw) { username = pw->pw_name; }
else { username = getenv("USER"); }
endpwent();
}
while (1)
@ -82,15 +79,11 @@ Result<int> luna_main(int argc, char** argv)
if (interactive)
{
auto cwd = TRY(os::FileSystem::working_directory());
os::print("%s@%s:%s%c ", username, hostname, cwd.chars(), prompt_end);
printf("%s@%s:%s%c ", username, hostname, cwd.chars(), prompt_end);
}
auto cmd = TRY(input_file->read_line());
if (cmd.is_empty())
{
if (interactive) puts("exit");
break;
}
if (cmd.is_empty()) break;
if (strspn(cmd.chars(), " \n") == cmd.length()) continue;

View File

@ -1,6 +1,4 @@
#include <os/ArgumentParser.h>
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <stdio.h>
#include <sys/stat.h>
@ -11,9 +9,6 @@ static const char* file_type(mode_t mode)
case S_IFREG: return "regular file";
case S_IFDIR: return "directory";
case S_IFCHR: return "character special device";
case S_IFBLK: return "block special device";
case S_IFLNK: return "symbolic link";
case S_IFIFO: return "pipe";
default: return "unknown file type";
}
}
@ -21,25 +16,23 @@ static const char* file_type(mode_t mode)
Result<int> luna_main(int argc, char** argv)
{
StringView path;
bool follow_symlinks { false };
os::ArgumentParser parser;
parser.add_description("Display file status.");
parser.add_system_program_info("stat"_sv);
parser.add_positional_argument(path, "path", true);
parser.add_switch_argument(follow_symlinks, 'L', "dereference"_sv, "follow symlinks");
parser.parse(argc, argv);
struct stat st;
TRY(os::FileSystem::stat(path, st, follow_symlinks));
char buf[11];
os::format_mode(st.st_mode, buf);
if (stat(path.chars(), &st) < 0)
{
perror("stat");
return 1;
}
printf(" File: %s\n", path.chars());
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));
printf("Inode: %lu Links: %lu\n", st.st_ino, st.st_nlink);
printf(" Mode: (%#o/%s) UID: %u GID: %u\n", st.st_mode & ~S_IFMT, buf, st.st_uid, st.st_gid);
printf(" Mode: %#o UID: %u GID: %u\n", st.st_mode & ~S_IFMT, st.st_uid, st.st_gid);
return 0;
}

View File

@ -33,7 +33,7 @@ char* getpass()
return nullptr;
}
static char buf[BUFSIZ];
static char buf[1024];
char* rc = fgets(buf, sizeof(buf), stdin);
restore_terminal();
@ -54,40 +54,33 @@ char* getpass()
Result<int> luna_main(int argc, char** argv)
{
StringView name;
bool prompt_password;
bool login;
if (geteuid() != 0)
{
fprintf(stderr, "%s must be setuid root!\n", argv[0]);
fprintf(stderr, "su must be setuid root!\n");
return 1;
}
os::ArgumentParser parser;
parser.add_description("Switch to a different user (by default, root)."_sv);
parser.add_system_program_info("su"_sv);
parser.add_positional_argument(name, "name"_sv, "root"_sv);
parser.add_switch_argument(prompt_password, 'p', "prompt", "prompt for a password even if running as root");
parser.add_switch_argument(login, 'l', "login"_sv, "change directory to the user's home and start a login shell");
parser.parse(argc, argv);
struct passwd* entry = getpwnam(name.chars());
if (!entry)
{
fprintf(stderr, "%s: user %s not found!\n", argv[0], name.chars());
fprintf(stderr, "su: user %s not found!\n", name.chars());
return 1;
}
endpwent();
if ((prompt_password || getuid() != geteuid()) && *entry->pw_passwd)
if (getuid() != geteuid() && *entry->pw_passwd)
{
char* pass = getpass();
if (!pass) return 1;
if (strcmp(pass, entry->pw_passwd))
{
fprintf(stderr, "%s: wrong password!\n", argv[0]);
fprintf(stderr, "Wrong password!\n");
return 1;
}
@ -97,20 +90,9 @@ Result<int> luna_main(int argc, char** argv)
setgid(entry->pw_gid);
setuid(entry->pw_uid);
if (login)
{
chdir(entry->pw_dir);
clearenv();
setenv("PATH", "/bin:/sbin", 1);
}
if (login || entry->pw_uid != 0) setenv("USER", entry->pw_name, 1);
setenv("HOME", entry->pw_dir, 1);
setenv("SHELL", entry->pw_shell, 1);
chdir(entry->pw_dir);
execl(entry->pw_shell, entry->pw_shell, NULL);
perror("execl");
return 1;
}

View File

@ -1,45 +0,0 @@
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/Process.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/wait.h>
Result<int> luna_main(int argc, char** argv)
{
Vector<StringView> command;
os::ArgumentParser parser;
parser.add_description("Time a command.");
parser.add_system_program_info("time"_sv);
parser.set_vector_argument(command, true);
TRY(parser.parse(argc, argv));
auto pid = TRY(os::Process::fork());
if (pid == 0)
{
TRY(os::Process::exec(command[0], command.slice()));
unreachable();
}
if (waitpid(pid, nullptr, 0) < 0)
{
perror("waitpid");
return 1;
}
struct rusage usage;
if (getrusage(RUSAGE_CHILDREN, &usage) < 0)
{
perror("getrusage");
return 1;
}
auto cmdline = TRY(String::join(command, " "));
os::println("%s %d.%.2ds user %d.%.2ds system"_sv, cmdline.chars(), usage.ru_utime.tv_sec,
usage.ru_utime.tv_usec / 10000, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 10000);
return 0;
}

View File

@ -1,22 +0,0 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <sys/mount.h>
Result<int> luna_main(int argc, char** argv)
{
StringView target;
os::ArgumentParser parser;
parser.add_description("Unmount a file system.");
parser.add_system_program_info("umount"_sv);
parser.add_positional_argument(target, "mountpoint"_sv, true);
parser.parse(argc, argv);
if (umount(target.chars()) < 0)
{
perror("umount");
return 1;
}
return 0;
}

View File

@ -24,7 +24,6 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Print system information. If given no options, defaults to -s."_sv);
parser.add_system_program_info("uname"_sv);
parser.add_switch_argument(all, 'a', "all"_sv, "print all information"_sv);
parser.add_switch_argument(kernel_name, 's', "kernel-name"_sv, "print the kernel name"_sv);
parser.add_switch_argument(node_name, 'n', "nodename"_sv, "print the network hostname"_sv);

View File

@ -1,3 +0,0 @@
root:!:0:
users:!:1:
selene:!:1000:

View File

@ -1,3 +0,0 @@
Name=motd
Command=/bin/cat /etc/motd
Wait=true

View File

@ -1,3 +0,0 @@
Name=login
Command=/bin/login
Restart=true

4
initrd/etc/init/99-shell Normal file
View File

@ -0,0 +1,4 @@
Name=shell
Command=/bin/sh
Restart=true
Environment=PATH=/bin:/sbin

View File

@ -1,4 +1 @@
Welcome to the Luna system!
Have a look around, you can run 'ls /bin' to list available commands.
You can use the username 'selene' with the password 'moon' to start a user session.
Hello, world!

View File

@ -31,27 +31,22 @@ set(SOURCES
src/sys/file.cpp
src/sys/id.cpp
src/sys/mkdir.cpp
src/sys/pstat.cpp
src/sys/mknod.cpp
src/sys/waitpid.cpp
src/sys/getdents.cpp
src/sys/stat.cpp
src/sys/chdir.cpp
src/sys/link.cpp
src/sys/uname.cpp
src/sys/mount.cpp
src/sys/resource.cpp
src/fs/VFS.cpp
src/fs/Pipe.cpp
src/fs/Mount.cpp
src/fs/tmpfs/FileSystem.cpp
src/fs/devices/DeviceRegistry.cpp
src/fs/devices/NullDevice.cpp
src/fs/devices/ZeroDevice.cpp
src/fs/devices/FullDevice.cpp
src/fs/devices/ConsoleDevice.cpp
src/fs/devices/FramebufferDevice.cpp
src/fs/InitRD.cpp
src/thread/ELF.cpp
src/InitRD.cpp
src/ELF.cpp
)
if("${LUNA_ARCH}" MATCHES "x86_64")

View File

@ -63,7 +63,7 @@ namespace ELFLoader
if (elf_header.e_machine != EM_MACH)
{
kerrorln("Error while loading ELF: ELF object's target architecture does not match the current one (%s)",
CPU::platform_string().chars());
CPU::platform_string());
return err(ENOEXEC);
}

View File

@ -18,7 +18,7 @@ void InitRD::initialize()
g_initrd.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
}
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode, uid_t uid, gid_t gid)
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
{
auto rc = VFS::create_directory(path, Credentials {});
if (rc.has_error())
@ -26,9 +26,7 @@ static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode,
if (rc.error() == EEXIST) return {};
return rc.release_error();
}
auto dir = rc.value();
dir->chmod(mode & (mode_t)~S_IFMT);
dir->chown(uid, gid);
rc.value()->chmod(mode & (mode_t)~S_IFMT);
return {};
}
@ -39,14 +37,13 @@ Result<void> InitRD::populate_vfs()
{
if (entry.type == TarStream::EntryType::RegularFile)
{
auto file = TRY(VFS::create_file(entry.name.chars(), Credentials {}));
auto file = TRY(VFS::create_file(entry.name, Credentials {}));
file->write(entry.data(), 0, entry.size);
file->chmod(entry.mode & (mode_t)~S_IFMT);
file->chown(entry.uid, entry.gid);
}
else if (entry.type == TarStream::EntryType::Directory)
{
TRY(vfs_create_dir_if_not_exists(entry.name.chars(), entry.mode, entry.uid, entry.gid));
TRY(vfs_create_dir_if_not_exists(entry.name, entry.mode));
}
}

View File

@ -1,13 +1,12 @@
#pragma once
#include <luna/Result.h>
#include <luna/StringView.h>
struct Registers;
namespace CPU
{
Result<StringView> identify();
StringView platform_string();
Result<const char*> identify();
const char* platform_string();
void platform_init();
void platform_finish_init();
@ -29,5 +28,7 @@ namespace CPU
u16 get_processor_id();
[[noreturn]] void bootstrap_switch_stack(u64 stack, void* function);
void pause();
}

View File

@ -58,7 +58,7 @@ namespace Timer
{
void tick()
{
timer_ticks += ARCH_TIMER_RESOLUTION;
timer_ticks++;
}
usize raw_ticks()
@ -68,17 +68,20 @@ namespace Timer
usize ticks()
{
return ticks_us() / US_PER_SECOND;
return ticks_ms() / 1000;
}
usize ticks_ms()
{
return timer_ticks / 1000;
return timer_ticks / ARCH_TIMER_FREQ;
}
usize ticks_us()
usize ticks_us() // We want a bit of precision; if there are 10 ticks/ms, do not return the truncated ms value *
// 1000, but ticks * 100 (1000/10), which is more precise
{
return timer_ticks;
if constexpr (ARCH_TIMER_FREQ > 1000) return timer_ticks / (ARCH_TIMER_FREQ / 1000);
else
return timer_ticks * (1000 / ARCH_TIMER_FREQ);
}
usize ticks_ns()
@ -88,22 +91,22 @@ namespace Timer
usize boot()
{
return boot_timestamp / US_PER_SECOND;
return boot_timestamp;
}
usize boot_ms()
{
return boot_timestamp / 1000;
return boot_timestamp * MS_PER_SECOND;
}
usize boot_us()
{
return boot_timestamp;
return boot_timestamp * US_PER_SECOND;
}
usize boot_ns()
{
return boot_timestamp * 1000;
return boot_timestamp * NS_PER_SECOND;
}
usize clock()
@ -128,12 +131,14 @@ namespace Timer
void init()
{
boot_timestamp = bootloader_time_to_unix(bootboot.datetime) * US_PER_SECOND;
boot_timestamp = bootloader_time_to_unix(bootboot.datetime);
arch_init();
}
}
static_assert(IsPowerOfTwo<usize, ARCH_TIMER_FREQ>, "ARCH_TIMER_FREQ must be a power of two");
bool should_invoke_scheduler()
{
return (timer_ticks % 1000) == 0;
return (timer_ticks % ARCH_TIMER_FREQ) == 0;
}

View File

@ -26,8 +26,6 @@ extern void pic_eoi(unsigned char irq);
extern void pic_eoi(Registers* regs);
extern void setup_idt();
static Thread* g_io_thread;
void FPData::save()
{
asm volatile("fxsave (%0)" : : "r"(m_data));
@ -128,10 +126,10 @@ void io_thread()
while (true)
{
u8 scancode;
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
while (!scancode_queue.try_pop(scancode)) { kernel_sleep(10); }
char key;
if (Keyboard::decode_scancode(scancode).try_set_value(key)) ConsoleDevice::did_press_key(key);
if (Keyboard::decode_scancode(scancode).try_set_value(key)) { ConsoleDevice::did_press_key(key); }
}
}
@ -149,7 +147,6 @@ extern "C" void arch_interrupt_entry(Registers* regs)
{
u8 scancode = IO::inb(0x60);
scancode_queue.try_push(scancode);
g_io_thread->wake_up();
pic_eoi(regs);
}
else if (regs->isr == 66) // System call
@ -187,7 +184,7 @@ static bool test_nx()
namespace CPU
{
Result<StringView> identify()
Result<const char*> identify()
{
static char brand_string[49];
@ -201,12 +198,12 @@ namespace CPU
brand_string[48] = 0; // null-terminate it :)
return StringView { brand_string, 48 };
return brand_string;
}
StringView platform_string()
const char* platform_string()
{
return "x86_64"_sv;
return "x86_64";
}
void platform_init()
@ -222,8 +219,8 @@ namespace CPU
void platform_finish_init()
{
g_io_thread = Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
.expect_value("Could not create the IO background thread!");
Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
.expect_value("Could not create the IO background thread!");
remap_pic();
}
@ -276,9 +273,8 @@ namespace CPU
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
{
StackFrame* current_frame = (StackFrame*)base_pointer;
while (current_frame &&
MemoryManager::validate_access(current_frame, sizeof(*current_frame), MemoryManager::DEFAULT_ACCESS) &&
current_frame->instruction)
// FIXME: Validate that the frame itself is readable, might span across multiple pages
while (current_frame && MemoryManager::validate_readable_page((u64)current_frame) && current_frame->instruction)
{
callback(current_frame->instruction, arg);
current_frame = current_frame->next;
@ -326,6 +322,15 @@ namespace CPU
&frame_index);
}
[[noreturn]] void bootstrap_switch_stack(u64 stack, void* function)
{
asm volatile("mov %0, %%rsp\n"
"jmp *%1"
:
: "r"(stack), "r"(function));
__builtin_unreachable();
}
void pause()
{
asm volatile("pause");

View File

@ -7,8 +7,8 @@ const u64 base_frequency = 1193182;
void Timer::arch_init()
{
constexpr u16 divisor = (u16)(base_frequency / ((1000 / ARCH_TIMER_RESOLUTION) * 1000));
static_assert(divisor >= 100, "ARCH_TIMER_RESOLUTION is too low");
constexpr u16 divisor = (u16)(base_frequency / (ARCH_TIMER_FREQ * 1000));
static_assert(divisor >= 100, "ARCH_TIMER_FREQ is too high");
IO::outb(PIT_CHANNEL_0, (u8)(divisor & 0xFF));
IO::outb(0x80, 0); // short delay
IO::outb(PIT_CHANNEL_0, (u8)((divisor & 0xFF00) >> 8));

View File

@ -1,5 +1,4 @@
#pragma once
#include <luna/Types.h>
// Every timer tick is equivalent to 250 microseconds.
const usize ARCH_TIMER_RESOLUTION = 250;
const usize ARCH_TIMER_FREQ = 4;

View File

@ -1,8 +1,8 @@
#include "boot/Init.h"
#include "InitRD.h"
#include "Log.h"
#include "arch/CPU.h"
#include "boot/bootboot.h"
#include "fs/InitRD.h"
#include "memory/MemoryManager.h"
#include "video/Framebuffer.h"
#include <luna/CString.h>

View File

@ -1,26 +0,0 @@
#include "fs/Mount.h"
LinkedList<MountInode> g_mounts;
Result<SharedPtr<VFS::Inode>> MountInode::create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs)
{
auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) MountInode()));
inode->m_source = source;
inode->m_mountee = fs;
inode->m_mount_root_inode = fs->root_inode();
auto parent = TRY(source->find(".."));
TRY(fs->set_mount_dir(parent));
source->add_handle();
g_mounts.append(inode.ptr());
return (SharedPtr<VFS::Inode>)inode;
}
MountInode::~MountInode()
{
m_source->remove_handle();
}

View File

@ -1,155 +0,0 @@
#pragma once
#include "fs/VFS.h"
#include <luna/LinkedList.h>
class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
{
public:
static Result<SharedPtr<VFS::Inode>> create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs);
void set_fs(SharedPtr<VFS::FileSystem> fs)
{
m_mountee = fs;
}
Result<SharedPtr<VFS::Inode>> find(const char* name) const override
{
return m_mount_root_inode->find(name);
}
Option<VFS::DirectoryEntry> get(usize index) const override
{
return m_mount_root_inode->get(index);
}
Result<usize> read(u8*, usize, usize) const override
{
return err(EISDIR);
}
Result<usize> write(const u8*, usize, usize) override
{
return err(EISDIR);
}
Result<void> truncate(usize) override
{
return err(EISDIR);
}
bool blocking() const override
{
return false;
}
usize size() const override
{
return 0;
}
mode_t mode() const override
{
return m_mount_root_inode->mode();
}
u32 uid() const override
{
return m_mount_root_inode->uid();
}
u32 gid() const override
{
return m_mount_root_inode->gid();
}
nlink_t nlinks() const override
{
return m_mount_root_inode->nlinks();
}
Result<void> chmod(mode_t mode) override
{
return m_mount_root_inode->chmod(mode);
}
Result<void> chown(u32 uid, u32 gid) override
{
return m_mount_root_inode->chown(uid, gid);
}
VFS::FileSystem* fs() const override
{
return m_mountee.ptr();
}
usize inode_number() const override
{
return m_mount_root_inode->inode_number();
}
VFS::InodeType type() const override
{
return VFS::InodeType::Directory;
}
void did_link() override
{
m_mount_root_inode->did_link();
}
void did_unlink() override
{
m_mount_root_inode->did_unlink();
}
usize entries() const override
{
return m_mount_root_inode->entries();
}
bool is_mountpoint() const override
{
return true;
}
SharedPtr<VFS::Inode> source() const
{
return m_source;
}
Result<void> remove_entry(const char* name) override
{
return m_mount_root_inode->remove_entry(name);
}
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override
{
return m_mount_root_inode->create_file(name);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
{
return m_mount_root_inode->create_subdirectory(name);
}
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override
{
return m_mount_root_inode->add_entry(inode, name);
}
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name) override
{
return m_mount_root_inode->replace_entry(inode, name);
}
virtual ~MountInode();
private:
SharedPtr<VFS::Inode> m_source;
SharedPtr<VFS::FileSystem> m_mountee;
SharedPtr<VFS::Inode> m_mount_root_inode;
MountInode() = default;
};
extern LinkedList<MountInode> g_mounts;

View File

@ -1,62 +0,0 @@
#include "fs/Pipe.h"
#include "thread/Scheduler.h"
Result<void> Pipe::create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe)
{
auto pipe = TRY(make_shared<Pipe>());
auto writer = TRY(make_shared<PipeWriter>());
auto reader = TRY(make_shared<PipeReader>());
auto auth = Scheduler::current()->auth;
pipe->m_writer = writer.ptr();
pipe->m_reader = reader.ptr();
writer->m_pipe = reader->m_pipe = pipe;
writer->chown(auth.euid, auth.egid);
reader->chown(auth.euid, auth.egid);
rpipe = reader;
wpipe = writer;
return {};
}
Result<usize> Pipe::read(u8* buf, usize, usize length)
{
if (length > m_data_buffer.size()) length = m_data_buffer.size();
memcpy(buf, m_data_buffer.data(), length);
memmove(m_data_buffer.data(), m_data_buffer.data() + length, m_data_buffer.size() - length);
TRY(m_data_buffer.try_resize(m_data_buffer.size() - length));
return length;
}
Result<usize> Pipe::write(const u8* buf, usize, usize length)
{
if (!m_reader) return length;
u8* slice = TRY(m_data_buffer.slice_at_end(length));
memcpy(slice, buf, length);
return length;
}
bool Pipe::blocking() const
{
return !m_data_buffer.size() && m_writer;
}
PipeWriter::~PipeWriter()
{
m_pipe->m_writer = nullptr;
}
PipeReader::~PipeReader()
{
m_pipe->m_reader = nullptr;
}

View File

@ -1,139 +0,0 @@
#pragma once
#include "fs/VFS.h"
#include <luna/Buffer.h>
class PipeInodeBase;
class PipeReader;
class PipeWriter;
class Pipe
{
public:
static Result<void> create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe);
Result<usize> read(u8* buf, usize, usize length);
Result<usize> write(const u8* buf, usize, usize length);
bool blocking() const;
private:
Buffer m_data_buffer;
PipeWriter* m_writer;
PipeReader* m_reader;
friend class PipeInodeBase;
friend class PipeReader;
friend class PipeWriter;
};
class PipeInodeBase : public VFS::FileInode
{
public:
VFS::InodeType type() const override
{
return VFS::InodeType::FIFO;
}
Result<void> truncate(usize) override
{
return err(ENOTSUP);
}
bool blocking() const override
{
return m_pipe->blocking();
}
usize size() const override
{
return m_pipe->m_data_buffer.size();
}
void did_link() override
{
}
void did_unlink() override
{
}
Result<void> chmod(mode_t) override
{
return err(ENOTSUP);
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
usize inode_number() const override
{
return 0;
}
VFS::FileSystem* fs() const override
{
return nullptr;
}
virtual ~PipeInodeBase() = default;
friend class Pipe;
protected:
SharedPtr<Pipe> m_pipe;
u32 m_uid { 0 };
u32 m_gid { 0 };
};
class PipeWriter : public PipeInodeBase
{
public:
Result<usize> read(u8*, usize, usize) const override
{
// A FileDescriptor pointing to a PipeWriter should never be opened for reading.
check(false);
}
Result<usize> write(const u8* buf, usize offset, usize length) override
{
return m_pipe->write(buf, offset, length);
}
mode_t mode() const override
{
return 0200;
}
~PipeWriter();
friend class Pipe;
};
class PipeReader : public PipeInodeBase
{
public:
Result<usize> read(u8* buf, usize offset, usize length) const override
{
return m_pipe->read(buf, offset, length);
}
Result<usize> write(const u8*, usize, usize) override
{
// A FileDescriptor pointing to a PipeReader should never be opened for writing.
check(false);
}
mode_t mode() const override
{
return 0400;
}
~PipeReader();
friend class Pipe;
};

View File

@ -1,6 +1,5 @@
#include "fs/VFS.h"
#include "Log.h"
#include "fs/Mount.h"
#include "thread/Thread.h"
#include <bits/modes.h>
#include <luna/PathParser.h>
@ -15,60 +14,28 @@ namespace VFS
return *root_fs->root_inode();
}
static constexpr int MAX_SYMLINKS = 8;
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, SharedPtr<Inode> current_inode,
bool follow_last_symlink, int& symlinks_followed)
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
{
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
if (*path == '\0') return err(ENOENT);
auto parser = TRY(PathParser::create(path));
SharedPtr<Inode> parent_inode = current_inode;
SharedPtr<Inode> current_inode;
if (parser.is_absolute() || !working_directory) current_inode = root_fs->root_inode();
else
current_inode = working_directory;
const char* section;
while (parser.next().try_set_value(section))
{
if (!can_execute(current_inode, auth)) return err(EACCES);
current_inode = TRY(current_inode->find(section));
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
{
auto link = TRY(current_inode->readlink());
SharedPtr<VFS::Inode> symlink_root;
if (PathParser::is_absolute(link.chars())) symlink_root = root_fs->root_inode();
else
symlink_root = parent_inode;
symlinks_followed++;
current_inode = TRY(resolve_path_impl(link.chars(), auth, symlink_root, true, symlinks_followed));
symlinks_followed--;
}
parent_inode = current_inode;
}
return current_inode;
}
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
bool follow_last_symlink)
{
SharedPtr<Inode> current_inode;
if (PathParser::is_absolute(path) || !working_directory) current_inode = root_fs->root_inode();
else
current_inode = working_directory;
int symlinks_followed = 0;
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
}
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
{
auto parser = TRY(PathParser::create(path));
@ -130,8 +97,6 @@ namespace VFS
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
@ -140,8 +105,6 @@ namespace VFS
bool can_write(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
@ -150,8 +113,6 @@ namespace VFS
bool can_read(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
@ -167,61 +128,4 @@ namespace VFS
{
return inode->mode() & S_ISGID;
}
bool is_seekable(SharedPtr<Inode> inode)
{
return inode->type() != InodeType::FIFO && inode->type() != InodeType::CharacterDevice;
}
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs)
{
root_fs = fs;
return {};
}
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<VFS::Inode> working_directory)
{
auto parser = TRY(PathParser::create(path));
auto parent_path = TRY(parser.dirname());
auto child = TRY(parser.basename());
kdbgln("vfs: Mounting filesystem on target %s", path);
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (inode->is_mountpoint()) return err(EBUSY);
auto mount = TRY(MountInode::create(inode, fs));
TRY(parent_inode->replace_entry(mount, child.chars()));
return {};
}
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
{
auto parser = TRY(PathParser::create(path));
auto parent_path = TRY(parser.dirname());
auto child = TRY(parser.basename());
kdbgln("vfs: Unmounting filesystem on target %s", path);
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (!inode->is_mountpoint()) return err(EINVAL);
// There are still open file descriptors referencing files within this file system.
if (inode->fs()->handles() != 0) return err(EBUSY);
auto mount = (MountInode*)inode.ptr();
TRY(parent_inode->replace_entry(mount->source(), child.chars()));
g_mounts.remove(mount);
return {};
}
}

View File

@ -1,5 +1,4 @@
#pragma once
#include <bits/makedev.h>
#include <luna/SharedPtr.h>
#include <luna/StaticString.h>
#include <luna/StringView.h>
@ -13,52 +12,12 @@ namespace VFS
{
RegularFile,
Directory,
CharacterDevice,
BlockDevice,
Symlink,
FIFO,
Device
};
class FileSystem;
class Inode;
class FileSystem
{
public:
virtual SharedPtr<Inode> root_inode() const = 0;
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
virtual Result<SharedPtr<Inode>> create_symlink_inode(StringView link) = 0;
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
virtual u64 handles() const
{
return m_handles;
}
virtual void add_handle()
{
m_handles++;
}
virtual void remove_handle()
{
m_handles--;
}
virtual dev_t host_device_id() const = 0;
virtual ~FileSystem() = default;
protected:
u64 m_handles { 0 };
};
struct DirectoryEntry
{
public:
@ -87,8 +46,6 @@ namespace VFS
virtual Result<void> remove_entry(const char* name) = 0;
virtual Result<void> replace_entry(SharedPtr<Inode> inode, const char* name) = 0;
virtual usize entries() const = 0;
// File-specific methods
@ -100,27 +57,11 @@ namespace VFS
virtual bool blocking() const = 0;
// Symlink-specific methods
virtual Result<StringView> readlink()
{
return StringView {};
}
virtual dev_t device_id() const
{
return luna_dev_makedev(0, 0);
}
// Metadata accessors
virtual usize size() const = 0;
virtual mode_t mode() const = 0;
virtual nlink_t nlinks() const
{
return 1;
}
virtual u32 uid() const
{
return 0;
@ -131,11 +72,6 @@ namespace VFS
return 0;
}
virtual bool is_mountpoint() const
{
return false;
}
virtual void did_link() = 0;
virtual void did_unlink() = 0;
@ -145,25 +81,13 @@ namespace VFS
virtual Result<void> chown(u32 uid, u32 gid) = 0;
// Generic VFS-related methods
virtual FileSystem* fs() const = 0;
virtual FileSystem& fs() const = 0;
virtual ~Inode() = default;
virtual InodeType type() const = 0;
virtual usize inode_number() const = 0;
virtual void add_handle()
{
auto* f = fs();
if (f) f->add_handle();
}
virtual void remove_handle()
{
auto* f = fs();
if (f) f->remove_handle();
}
};
class FileInode : Inode
@ -194,11 +118,6 @@ namespace VFS
return err(ENOTDIR);
}
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
{
return err(ENOTDIR);
}
Result<void> remove_entry(const char*) override
{
return err(ENOTDIR);
@ -250,11 +169,6 @@ namespace VFS
return err(ENOTDIR);
}
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
{
return err(ENOTDIR);
}
Result<void> remove_entry(const char*) override
{
return err(ENOTDIR);
@ -267,15 +181,30 @@ namespace VFS
InodeType type() const override
{
return InodeType::CharacterDevice;
return InodeType::Device;
}
virtual ~DeviceInode() = default;
};
class FileSystem
{
public:
virtual SharedPtr<Inode> root_inode() const = 0;
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
virtual ~FileSystem() = default;
};
extern SharedPtr<FileSystem> root_fs;
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {},
bool follow_last_symlink = true);
SharedPtr<VFS::Inode> working_directory = {});
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {});
@ -291,13 +220,5 @@ namespace VFS
bool is_setuid(SharedPtr<Inode> inode);
bool is_setgid(SharedPtr<Inode> inode);
bool is_seekable(SharedPtr<Inode> inode);
Inode& root_inode();
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<Inode> working_directory = {});
Result<void> umount(const char* path, Credentials auth, SharedPtr<Inode> working_directory = {});
}

View File

@ -1,24 +1,19 @@
#include "fs/devices/ConsoleDevice.h"
#include "Log.h"
#include "arch/Keyboard.h"
#include "fs/devices/DeviceRegistry.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
#include "video/TextConsole.h"
#include <bits/termios.h>
#include <luna/Buffer.h>
#include <luna/CString.h>
#include <luna/Units.h>
#include <luna/Vector.h>
static Buffer g_console_input;
static bool g_eof { false };
static bool g_echo { true };
Result<void> ConsoleDevice::create()
Result<SharedPtr<Device>> ConsoleDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, "console");
return (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
}
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
@ -64,28 +59,7 @@ void ConsoleDevice::did_press_key(char key)
// Ctrl+D
if (key == 'd' && (Keyboard::modifiers() & Keyboard::LeftControl))
{
if (g_temp_input.size() == 0) g_eof = true;
return;
}
if (key == 'e' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
{
Scheduler::dump_state();
return;
}
if (key == 'm' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
{
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
return;
}
if (key == 'h' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
{
dump_heap_usage();
g_eof = true;
return;
}

View File

@ -5,7 +5,7 @@ class ConsoleDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
static Result<SharedPtr<Device>> create();
Result<usize> read(u8*, usize, usize) const override;

View File

@ -18,11 +18,6 @@ class Device
return 0;
}
virtual bool is_block_device() const
{
return false;
}
virtual bool blocking() const = 0;
virtual ~Device() = default;

View File

@ -1,85 +1,53 @@
#include "fs/devices/DeviceRegistry.h"
#include "Log.h"
#include "fs/VFS.h"
#include "fs/devices/ConsoleDevice.h"
#include "fs/devices/FramebufferDevice.h"
#include "fs/devices/FullDevice.h"
#include "fs/devices/NullDevice.h"
#include "fs/devices/ZeroDevice.h"
#include "fs/tmpfs/FileSystem.h"
#include "thread/Thread.h"
#include <luna/Vector.h>
struct DeviceDescriptor
{
SharedPtr<Device> device;
device_create_func_t initializer;
u32 major;
u32 minor;
const char* name;
mode_t mode;
};
Vector<DeviceDescriptor> g_available_devices = {};
SharedPtr<VFS::FileSystem> g_device_fs;
namespace DeviceRegistry
{
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor)
Result<SharedPtr<Device>> create_special_device(u32 major, u32 minor)
{
for (const auto& descriptor : g_available_devices)
{
if (descriptor.major == major && descriptor.minor == minor) return descriptor.device;
if (descriptor.major == major && descriptor.minor == minor) return descriptor.initializer();
}
return err(ENODEV);
}
Result<void> create_special_device_inode(DeviceDescriptor& descriptor)
{
auto inode = TRY(g_device_fs->create_device_inode(descriptor.major, descriptor.minor));
inode->chmod(descriptor.mode);
TRY(g_device_fs->root_inode()->add_entry(inode, descriptor.name));
return {};
}
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name, mode_t mode)
Result<void> register_special_device(u32 major, u32 minor, device_create_func_t initializer)
{
for (const auto& descriptor : g_available_devices)
{
if (descriptor.major == major && descriptor.minor == minor) return err(EEXIST);
}
kdbgln("DeviceRegistry: registered new device type %u:%u at path /dev/%s", major, minor, name);
kdbgln("DeviceRegistry: registered new device type %u:%u", major, minor);
auto desc = DeviceDescriptor { .device = device, .major = major, .minor = minor, .name = name, .mode = mode };
TRY(g_available_devices.try_append(desc));
TRY(create_special_device_inode(desc));
return {};
return g_available_devices.try_append(
DeviceDescriptor { .initializer = initializer, .major = major, .minor = minor });
}
Result<void> init()
{
auto device_fs = TRY(TmpFS::FileSystem::create());
TRY(VFS::mount("/dev", device_fs, Credentials {}));
g_device_fs = device_fs;
NullDevice::create();
ZeroDevice::create();
FullDevice::create();
ConsoleDevice::create();
FramebufferDevice::create();
// Hardcode default devices.
TRY(register_special_device(DeviceMajorTypes::Memory, 0, NullDevice::create));
TRY(register_special_device(DeviceMajorTypes::Memory, 1, ZeroDevice::create));
TRY(register_special_device(DeviceMajorTypes::Console, 0, ConsoleDevice::create));
TRY(register_special_device(DeviceMajorTypes::Framebuffer, 0, FramebufferDevice::create));
return {};
}
dev_t next_null_device_id()
{
static u32 next_minor = 0;
return luna_dev_makedev(0, next_minor++);
}
}

View File

@ -2,7 +2,8 @@
#include "fs/devices/Device.h"
#include <luna/SharedPtr.h>
#include <sys/types.h>
typedef Result<SharedPtr<Device>> (*device_create_func_t)(void);
namespace DeviceRegistry
{
@ -14,13 +15,9 @@ namespace DeviceRegistry
Framebuffer = 3,
};
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
Result<SharedPtr<Device>> create_special_device(u32 major, u32 minor);
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name,
mode_t mode = 0666);
Result<void> register_special_device(u32 major, u32 minor, device_create_func_t initializer);
Result<void> init();
// Used for file systems (like tmpfs) that do not have a host device.
dev_t next_null_device_id();
}

View File

@ -3,10 +3,9 @@
#include <bits/ioctl-defs.h>
#include <luna/CString.h>
Result<void> FramebufferDevice::create()
Result<SharedPtr<Device>> FramebufferDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<FramebufferDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device, "fb0", 0600);
return (SharedPtr<Device>)TRY(make_shared<FramebufferDevice>());
}
Result<usize> FramebufferDevice::read(u8*, usize, usize) const

View File

@ -5,7 +5,7 @@ class FramebufferDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
static Result<SharedPtr<Device>> create();
Result<usize> read(u8*, usize, usize) const override;
@ -13,11 +13,6 @@ class FramebufferDevice : public Device
bool blocking() const override;
bool is_block_device() const override
{
return true;
}
Result<u64> ioctl(int request, void* arg) override;
usize size() const override;

View File

@ -1,7 +0,0 @@
#include "fs/devices/FullDevice.h"
Result<void> FullDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<FullDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 2, device, "full");
}

View File

@ -1,28 +0,0 @@
#pragma once
#include "fs/devices/DeviceRegistry.h"
class FullDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
Result<usize> read(u8* buf, usize, usize length) const override
{
__builtin_memset(buf, 0, length);
return length;
}
Result<usize> write(const u8*, usize, usize) override
{
return err(ENOSPC);
}
bool blocking() const override
{
return false;
}
virtual ~FullDevice() = default;
};

View File

@ -1,7 +1,6 @@
#include "fs/devices/NullDevice.h"
Result<void> NullDevice::create()
Result<SharedPtr<Device>> NullDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<NullDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device, "null");
return (SharedPtr<Device>)TRY(make_shared<NullDevice>());
}

View File

@ -5,7 +5,7 @@ class NullDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
static Result<SharedPtr<Device>> create();
Result<usize> read(u8*, usize, usize) const override
{

View File

@ -1,7 +1,6 @@
#include "fs/devices/ZeroDevice.h"
Result<void> ZeroDevice::create()
Result<SharedPtr<Device>> ZeroDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<ZeroDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device, "zero");
return (SharedPtr<Device>)TRY(make_shared<ZeroDevice>());
}

View File

@ -5,7 +5,7 @@ class ZeroDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
static Result<SharedPtr<Device>> create();
Result<usize> read(u8* buf, usize, usize length) const override
{

View File

@ -1,5 +1,4 @@
#include "fs/tmpfs/FileSystem.h"
#include "fs/Mount.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/Alloc.h>
#include <luna/CString.h>
@ -24,15 +23,6 @@ namespace TmpFS
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_symlink_inode(StringView link)
{
SharedPtr<SymlinkInode> inode = TRY(make_shared<SymlinkInode>());
inode->set_fs(*this, {});
TRY(inode->set_link(link, {}));
inode->set_inode_number(m_next_inode_number++, {});
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
{
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
@ -49,29 +39,16 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
{
SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));
SharedPtr<Device> device = TRY(DeviceRegistry::create_special_device(major, minor));
SharedPtr<DeviceInode> inode = TRY(make_shared<DeviceInode>());
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->set_device(device, {});
// FIXME: This should be queried from Device directly, but Device doesn't have an API to store and retrieve its
// device ID atm.
inode->set_device_id(luna_dev_makedev(major, minor), {});
return (SharedPtr<VFS::Inode>)inode;
}
FileSystem::FileSystem()
{
m_host_device_id = DeviceRegistry::next_null_device_id();
}
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
{
return m_root_inode->replace_entry(parent, "..");
}
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
{
m_root_inode = root;
@ -87,20 +64,6 @@ namespace TmpFS
return err(ENOENT);
}
Result<void> DirInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
for (auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars()))
{
entry.inode = inode;
return {};
}
}
return err(ENOENT);
}
Option<VFS::DirectoryEntry> DirInode::get(usize index) const
{
if (index >= m_entries.size()) return {};
@ -127,8 +90,6 @@ namespace TmpFS
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
if (inode->is_mountpoint()) return err(EBUSY);
m_entries.remove_first_matching(
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });

View File

@ -5,7 +5,6 @@
#include <luna/Badge.h>
#include <luna/Buffer.h>
#include <luna/StaticString.h>
#include <luna/String.h>
#include <luna/Vector.h>
namespace TmpFS
@ -21,29 +20,19 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_file_inode() override;
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView link) override;
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
static Result<SharedPtr<VFS::FileSystem>> create();
dev_t host_device_id() const override
{
return m_host_device_id;
}
virtual ~FileSystem() = default;
private:
FileSystem();
FileSystem() = default;
void set_root(SharedPtr<VFS::Inode> root);
SharedPtr<VFS::Inode> m_root_inode;
Atomic<usize> m_next_inode_number { 2 };
dev_t m_host_device_id;
};
class FileInode : public VFS::FileInode
@ -61,9 +50,9 @@ namespace TmpFS
m_inode_number = inum;
}
VFS::FileSystem* fs() const override
VFS::FileSystem& fs() const override
{
return m_fs;
return *m_fs;
}
usize inode_number() const override
@ -94,11 +83,6 @@ namespace TmpFS
return m_gid;
}
nlink_t nlinks() const override
{
return (nlink_t)m_nlinks;
}
Result<void> chmod(mode_t mode) override
{
m_mode = mode;
@ -134,130 +118,11 @@ namespace TmpFS
u32 m_nlinks { 0 };
};
class SymlinkInode : public VFS::FileInode
{
public:
SymlinkInode() = default;
VFS::InodeType type() const override
{
return VFS::InodeType::Symlink;
}
void set_fs(FileSystem& fs, Badge<FileSystem>)
{
m_fs = &fs;
}
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_inode_number = inum;
}
Result<void> set_link(StringView link, Badge<FileSystem>)
{
m_link = TRY(String::from_string_view(link));
return {};
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8*, usize, usize) const override
{
return err(ENOTSUP);
}
Result<usize> write(const u8*, usize, usize) override
{
return err(ENOTSUP);
}
Result<void> truncate(usize) override
{
return err(ENOTSUP);
}
usize size() const override
{
return m_link.length();
}
mode_t mode() const override
{
return 0777;
}
u32 uid() const override
{
return m_uid;
}
u32 gid() const override
{
return m_gid;
}
nlink_t nlinks() const override
{
return (nlink_t)m_nlinks;
}
Result<void> chmod(mode_t) override
{
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
void did_link() override
{
m_nlinks++;
}
void did_unlink() override
{
m_nlinks--;
}
Result<StringView> readlink() override
{
return m_link.view();
}
virtual ~SymlinkInode() = default;
private:
VFS::FileSystem* m_fs;
String m_link;
usize m_inode_number;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
};
class DeviceInode : public VFS::DeviceInode
{
public:
DeviceInode() = default;
VFS::InodeType type() const override
{
return m_device->is_block_device() ? VFS::InodeType::BlockDevice : VFS::InodeType::CharacterDevice;
}
void set_fs(FileSystem& fs, Badge<FileSystem>)
{
m_fs = &fs;
@ -273,19 +138,9 @@ namespace TmpFS
m_device = device;
}
void set_device_id(dev_t id, Badge<FileSystem>)
VFS::FileSystem& fs() const override
{
m_device_id = id;
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
dev_t device_id() const override
{
return m_device_id;
return *m_fs;
}
usize inode_number() const override
@ -339,11 +194,6 @@ namespace TmpFS
return m_gid;
}
nlink_t nlinks() const override
{
return (nlink_t)m_nlinks;
}
Result<void> chmod(mode_t mode) override
{
m_mode = mode;
@ -377,7 +227,6 @@ namespace TmpFS
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
dev_t m_device_id { 0 };
};
class DirInode : public VFS::Inode
@ -456,9 +305,9 @@ namespace TmpFS
return {};
}
VFS::FileSystem* fs() const override
VFS::FileSystem& fs() const override
{
return m_fs;
return *m_fs;
}
usize inode_number() const override
@ -492,7 +341,6 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override;
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
virtual ~DirInode() = default;

View File

@ -1,14 +1,22 @@
#include "ELF.h"
#include "InitRD.h"
#include "Log.h"
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "arch/PCI.h"
#include "arch/Timer.h"
#include "boot/Init.h"
#include "config.h"
#include "fs/InitRD.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/tmpfs/FileSystem.h"
#include "memory/Heap.h"
#include "memory/KernelVM.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
#include "video/Framebuffer.h"
#include "video/TextConsole.h"
#include <luna/CString.h>
#include <luna/Result.h>
#include <luna/Units.h>
extern void set_host_name(StringView);
@ -23,7 +31,7 @@ void reap_thread()
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
kernel_wait_for_event();
kernel_sleep(250);
}
}
@ -34,24 +42,27 @@ Result<void> init()
// Default hostname if nobody from userspace changes it
set_host_name("moon"_sv);
kinfoln("Current platform: %s", CPU::platform_string().chars());
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());
kinfoln("Current platform: %s", CPU::platform_string());
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"));
Timer::init();
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
auto root = TRY(TmpFS::FileSystem::create());
TRY(VFS::mount_root(root));
TRY(InitRD::populate_vfs());
Thread::init();
Scheduler::init();
VFS::root_fs = TRY(TmpFS::FileSystem::create());
TRY(DeviceRegistry::init());
InitRD::populate_vfs();
auto init = TRY(VFS::resolve_path("/bin/init", Credentials {}));
auto init_thread = TRY(Scheduler::new_userspace_thread(init, "/bin/init"));
TRY(Scheduler::new_userspace_thread(init, "/bin/init"));
auto reap = Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
Scheduler::set_reap_thread(reap);
Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
PCI::scan(
[](const PCI::Device& device) {
@ -60,36 +71,43 @@ Result<void> init()
},
{ .klass = 1 });
CPU::platform_finish_init();
// Disable console logging before transferring control to userspace.
setup_log(log_debug_enabled(), log_serial_enabled(), false);
init_thread->wake_up();
CPU::enable_interrupts();
kernel_exit();
return {};
}
[[noreturn]] void init_wrapper()
{
auto rc = init();
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
kernel_exit();
CPU::idle_loop();
}
static constexpr u64 BOOTSTRAP_STACK_PAGES = 8;
// FIXME: Reclaim this memory as soon as we leave the init task (so as soon as the Scheduler runs a task switch)
static u64 allocate_initial_kernel_stack()
{
u64 address = MemoryManager::alloc_for_kernel(BOOTSTRAP_STACK_PAGES + 1, MMU::ReadWrite | MMU::NoExecute).value();
// First page is a guard page, the rest is stack.
MMU::unmap(address); // Unmap (without deallocating VM) one guard page so that attempts to access it fail with a
// non-present page fault.
// The actual stack.
Stack stack { address + ARCH_PAGE_SIZE, BOOTSTRAP_STACK_PAGES * ARCH_PAGE_SIZE };
return stack.top();
}
extern "C" [[noreturn]] void _start()
{
Init::check_magic();
Init::early_init();
Timer::init();
Thread::init();
Scheduler::init();
Scheduler::new_kernel_thread(init_wrapper, "[kinit]");
CPU::platform_finish_init();
CPU::enable_interrupts();
CPU::idle_loop();
u64 bootstrap_stack_top = allocate_initial_kernel_stack();
CPU::bootstrap_switch_stack(bootstrap_stack_top, (void*)init_wrapper);
}

View File

@ -396,25 +396,57 @@ namespace MemoryManager
return {};
}
bool validate_page_default_access(u64 address)
bool validate_readable_page(u64 address)
{
auto rc = MMU::get_flags(address);
if (rc.has_error()) return false;
return true;
}
bool validate_page_access(u64 address, int flags)
bool validate_writable_page(u64 address)
{
auto rc = MMU::get_flags(address);
if (rc.has_error()) return false;
if (rc.value() & flags) return true;
if (rc.value() & MMU::ReadWrite) return true;
return false;
}
bool validate_user_readable_page(u64 address)
{
auto rc = MMU::get_flags(address);
if (rc.has_error()) return false;
if (rc.value() & MMU::User) return true;
return false;
}
bool validate_user_writable_page(u64 address)
{
auto rc = MMU::get_flags(address);
if (rc.has_error()) return false;
if ((rc.value() & MMU::User) && (rc.value() && MMU::ReadWrite)) return true;
return false;
}
bool validate_userspace_string(u64 address)
{
if (!validate_user_readable_page(address)) return false;
while (*(char*)address != 0)
{
address++;
if (address % ARCH_PAGE_SIZE)
{
if (!validate_user_readable_page(address)) return false;
}
}
return true;
}
// FIXME: Make this more efficient.
Result<String> strdup_from_user(u64 address)
{
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
if (!validate_user_readable_page(address)) return err(EFAULT);
Vector<char> result;
@ -424,7 +456,7 @@ namespace MemoryManager
address++;
if (address % ARCH_PAGE_SIZE)
{
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
if (!validate_user_readable_page(address)) return err(EFAULT);
}
}
@ -433,26 +465,37 @@ namespace MemoryManager
return String::from_cstring(result.data());
}
bool validate_access(const void* mem, usize size, int flags)
bool validate_user_write(void* user, usize size)
{
uintptr_t address = (uintptr_t)mem;
uintptr_t page = align_down<ARCH_PAGE_SIZE>(address);
uintptr_t user_ptr = (uintptr_t)user;
uintptr_t user_page = align_down<ARCH_PAGE_SIZE>(user_ptr);
uintptr_t diff = address - page;
uintptr_t diff = user_ptr - user_page;
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
while (pages--)
{
if (flags > 0)
{
if (!validate_page_access(page, flags)) return false;
}
else
{
if (!validate_page_default_access(page)) return false;
}
page += ARCH_PAGE_SIZE;
if (!validate_user_writable_page(user_page)) return false;
user_page += ARCH_PAGE_SIZE;
}
return true;
}
bool validate_user_read(const void* user, usize size)
{
uintptr_t user_ptr = (uintptr_t)user;
uintptr_t user_page = align_down<ARCH_PAGE_SIZE>(user_ptr);
uintptr_t diff = user_ptr - user_page;
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
while (pages--)
{
if (!validate_user_readable_page(user_page)) return false;
user_page += ARCH_PAGE_SIZE;
}
return true;
@ -470,7 +513,7 @@ namespace MemoryManager
// Userspace pointer not aligned on page boundary
if (user_ptr != user_page)
{
if (!validate_page_access(user_page, MMU::ReadWrite | MMU::User)) return false;
if (!validate_user_writable_page(user_page)) return false;
}
while (size--)
@ -478,7 +521,7 @@ namespace MemoryManager
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
if (user_ptr % ARCH_PAGE_SIZE)
{
if (!validate_page_access(user_ptr, MMU::ReadWrite | MMU::User)) return false;
if (!validate_user_writable_page(user_ptr)) return false;
}
*(u8*)user_ptr = *kernel_ptr++;
@ -498,7 +541,7 @@ namespace MemoryManager
// Userspace pointer not aligned on page boundary
if (user_ptr != user_page)
{
if (!validate_page_access(user_page, MMU::User)) return false;
if (!validate_user_readable_page(user_page)) return false;
}
while (size--)
@ -506,7 +549,7 @@ namespace MemoryManager
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
if (user_ptr % ARCH_PAGE_SIZE)
{
if (!validate_page_access(user_ptr, MMU::User)) return false;
if (!validate_user_readable_page(user_ptr)) return false;
}
*kernel_ptr++ = *(const u8*)user_ptr;

View File

@ -1,13 +1,10 @@
#pragma once
#include "arch/MMU.h"
#include <luna/Result.h>
#include <luna/String.h>
#include <luna/Types.h>
namespace MemoryManager
{
constexpr int DEFAULT_ACCESS = 0;
void init();
Result<void> protect_kernel_sections();
@ -22,22 +19,17 @@ namespace MemoryManager
Result<void> remap(u64 address, usize count, int flags);
Result<void> remap_unaligned(u64 address, usize count, int flags);
bool validate_page_access(u64 address, int flags);
bool validate_page_default_access(u64 address);
bool validate_readable_page(u64 address);
bool validate_writable_page(u64 address);
bool validate_user_readable_page(u64 address);
bool validate_user_writable_page(u64 address);
bool validate_userspace_string(u64 address);
Result<String> strdup_from_user(u64 address);
bool validate_access(const void* mem, usize size, int flags);
inline bool validate_user_write(void* user, usize size)
{
return validate_access(user, size, MMU::ReadWrite | MMU::User);
}
inline bool validate_user_read(const void* user, usize size)
{
return validate_access(user, size, MMU::User);
}
bool validate_user_write(void* user, usize size);
bool validate_user_read(const void* user, usize size);
template <typename T> bool validate_user_write_typed(T* user)
{

View File

@ -14,10 +14,6 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
inode->add_handle();
if (current->current_directory) current->current_directory->remove_handle();
current->current_directory = inode;
current->current_directory_path = move(path);
@ -29,15 +25,11 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
auto old_wdir = current->current_directory_path.view();
String new_path = TRY(PathParser::join(old_wdir.is_empty() ? "/"_sv : old_wdir, path.view()));
inode->add_handle();
if (current->current_directory) current->current_directory->remove_handle();
current->current_directory = inode;
current->current_directory_path = move(new_path);

View File

@ -1,8 +1,8 @@
#include "ELF.h"
#include "Log.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/ELF.h"
#include "thread/Scheduler.h"
#include "thread/ThreadImage.h"
#include <bits/modes.h>
@ -31,37 +31,16 @@ static Result<Vector<String>> copy_string_vector_from_userspace(u64 address)
return result;
}
static u64 calculate_userspace_stack_size(const Vector<String>& v)
{
u64 total { 0 };
for (const auto& str : v)
{
// The string's byte count + a terminating NUL byte.
total += str.length() + 1;
// The pointer to said string in the userspace array.
total += sizeof(char*);
}
// The NULL pointer at the end of the userspace array.
total += sizeof(char*);
return total;
}
static constexpr usize MAX_ARGV_STACK_SIZE = 2 * ARCH_PAGE_SIZE;
Result<u64> sys_execve(Registers* regs, SyscallArgs args)
{
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
return err(E2BIG);
auto current = Scheduler::current();
// FIXME: Make sure argv & envp are not too big.
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
@ -88,11 +67,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
{
auto& descriptor = current->fd_table[i];
if (!descriptor.has_value()) continue;
if (descriptor->flags & O_CLOEXEC)
{
descriptor->inode->remove_handle();
descriptor = {};
}
if (descriptor->flags & O_CLOEXEC) descriptor = {};
}
MMU::delete_userspace_page_directory(current->directory);
@ -129,19 +104,14 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
thread->state = ThreadState::Runnable;
thread->is_kernel = false;
thread->parent_id = current->id;
thread->fp_data.save();
thread->name = current->name;
thread->auth = current->auth;
thread->current_directory = current->current_directory;
thread->current_directory_path = move(current_directory_path);
thread->umask = current->umask;
thread->parent = current;
for (int i = 0; i < FD_MAX; i++)
{
thread->fd_table[i] = current->fd_table[i];
if (current->fd_table[i].has_value()) current->fd_table[i]->inode->add_handle();
}
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
image->apply(thread);

View File

@ -7,21 +7,7 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
Scheduler::for_each_child(current, [](Thread* child) {
child->parent = Scheduler::init_thread();
return true;
});
auto* parent = current->parent;
if (parent && parent->state == ThreadState::Waiting)
{
auto child = *parent->child_being_waited_for;
if (child == -1 || child == (pid_t)current->id)
{
parent->child_being_waited_for = (pid_t)current->id;
parent->wake_up();
}
}
Scheduler::for_each_child((pid_t)current->id, [](Thread* child) { child->parent_id = 1; });
current->status = status;
current->state = ThreadState::Exited;

View File

@ -1,5 +1,4 @@
#include "Log.h"
#include "fs/Pipe.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
@ -33,7 +32,7 @@ Result<u64> sys_read(Registers*, SyscallArgs args)
usize nread = TRY(descriptor.inode->read(buf, descriptor.offset, size));
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nread;
descriptor.offset += nread;
return nread;
}
@ -52,11 +51,11 @@ Result<u64> sys_write(Registers*, SyscallArgs args)
if (!descriptor.is_writable()) return err(EBADF);
if (descriptor.should_append() && VFS::is_seekable(descriptor.inode)) descriptor.offset = descriptor.inode->size();
if (descriptor.should_append()) descriptor.offset = descriptor.inode->size();
usize nwritten = TRY(descriptor.inode->write(buf, descriptor.offset, size));
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nwritten;
descriptor.offset += nwritten;
return nwritten;
}
@ -71,10 +70,6 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
auto& descriptor = *TRY(current->resolve_fd(fd));
if (descriptor.inode->type() == VFS::InodeType::FIFO) return err(ESPIPE);
if (!VFS::is_seekable(descriptor.inode)) return descriptor.offset;
off_t new_offset;
switch (whence)
@ -152,58 +147,3 @@ Result<u64> sys_ioctl(Registers*, SyscallArgs args)
return descriptor.inode->ioctl(request, arg);
}
Result<u64> sys_dup2(Registers*, SyscallArgs args)
{
int oldfd = (int)args[0];
int newfd = (int)args[1];
Thread* current = Scheduler::current();
if (newfd < 0 || newfd >= FD_MAX) return err(EBADF);
auto descriptor = *TRY(current->resolve_fd(oldfd));
if (newfd == oldfd) return (u64)newfd;
current->fd_table[newfd] = descriptor;
current->fd_table[newfd]->flags &= ~O_CLOEXEC;
return (u64)newfd;
}
Result<u64> sys_pipe(Registers*, SyscallArgs args)
{
int* pfds = (int*)args[0];
Thread* current = Scheduler::current();
int rfd = TRY(current->allocate_fd(0));
int wfd = TRY(current->allocate_fd(rfd + 1));
if (!MemoryManager::copy_to_user_typed(pfds, &rfd)) return err(EFAULT);
if (!MemoryManager::copy_to_user_typed(pfds + 1, &wfd)) return err(EFAULT);
SharedPtr<VFS::Inode> rpipe;
SharedPtr<VFS::Inode> wpipe;
TRY(Pipe::create(rpipe, wpipe));
current->fd_table[rfd] = FileDescriptor { rpipe, 0, O_RDONLY };
current->fd_table[wfd] = FileDescriptor { wpipe, 0, O_WRONLY };
return 0;
}
Result<u64> sys_umask(Registers*, SyscallArgs args)
{
mode_t new_umask = (mode_t)args[0];
auto* current = Scheduler::current();
mode_t old_umask = current->umask;
current->umask = new_umask & 0777;
return old_umask;
}

View File

@ -1,7 +1,6 @@
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/atfile.h>
Result<u64> sys_getpid(Registers*, SyscallArgs)
{
@ -10,9 +9,7 @@ Result<u64> sys_getpid(Registers*, SyscallArgs)
Result<u64> sys_getppid(Registers*, SyscallArgs)
{
auto* parent = Scheduler::current()->parent;
return parent ? parent->id : 0;
return Scheduler::current()->parent_id;
}
Result<u64> sys_getuid(Registers*, SyscallArgs)
@ -95,37 +92,33 @@ Result<u64> sys_setegid(Registers*, SyscallArgs args)
return 0;
}
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
Result<u64> sys_chmod(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
mode_t mode = (mode_t)args[2];
int flags = (int)args[3];
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
mode_t mode = (mode_t)args[1];
auto* current = Scheduler::current();
Credentials& auth = Scheduler::current()->auth;
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
auto inode = TRY(VFS::resolve_path(path.chars(), auth, Scheduler::current()->current_directory));
if (current->auth.euid != 0 && current->auth.euid != inode->uid()) return err(EPERM);
if (auth.euid != 0 && auth.euid != inode->uid()) return err(EPERM);
TRY(inode->chmod(mode));
return 0;
}
Result<u64> sys_fchownat(Registers*, SyscallArgs args)
Result<u64> sys_chown(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
u32 uid = (u32)args[2];
u32 gid = (u32)args[3];
int flags = (int)args[4];
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
u32 uid = (u32)args[1];
u32 gid = (u32)args[2];
auto* current = Scheduler::current();
Credentials& auth = Scheduler::current()->auth;
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
auto inode = TRY(VFS::resolve_path(path.chars(), auth, Scheduler::current()->current_directory));
if (current->auth.euid != 0) return err(EPERM);
if (auth.euid != 0) return err(EPERM);
TRY(inode->chown(uid == (u32)-1 ? inode->uid() : uid, gid == (u32)-1 ? inode->gid() : gid));

View File

@ -1,8 +1,6 @@
#include "Log.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/atfile.h>
#include <luna/PathParser.h>
Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
@ -20,9 +18,7 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
if (basename.view() == ".") return err(EINVAL);
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false));
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
if (flags > 0)
@ -35,90 +31,3 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
return 0;
}
Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
{
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
int dirfd = (int)args[1];
auto linkpath = TRY(MemoryManager::strdup_from_user(args[2]));
if (target.is_empty()) return err(ENOENT);
auto* current = Scheduler::current();
auto parser = TRY(PathParser::create(linkpath.chars()));
auto parent = TRY(parser.dirname());
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
auto child_name = TRY(parser.basename());
TRY(VFS::validate_filename(child_name.view()));
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
TRY(inode->chown(current->auth.euid, current->auth.egid));
TRY(parent_inode->add_entry(inode, child_name.chars()));
return 0;
}
Result<u64> sys_readlinkat(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
char* buf = (char*)args[2];
usize bufsiz = (usize)args[3];
auto* current = Scheduler::current();
auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false));
if (symlink->type() != VFS::InodeType::Symlink) return err(EINVAL);
auto linkpath = TRY(symlink->readlink());
check(!linkpath.is_empty());
usize nread = linkpath.length();
if (nread > bufsiz) nread = bufsiz;
kdbgln("readlink: reading %zu bytes from symlink (%s)", nread, linkpath.chars());
if (!MemoryManager::copy_to_user(buf, linkpath.chars(), nread)) return err(EFAULT);
return nread;
}
Result<u64> sys_linkat(Registers*, SyscallArgs args)
{
int olddirfd = (int)args[0];
auto oldpath = TRY(MemoryManager::strdup_from_user(args[1]));
int newdirfd = (int)args[2];
auto newpath = TRY(MemoryManager::strdup_from_user(args[3]));
int flags = (int)args[4];
auto* current = Scheduler::current();
auto parser = TRY(PathParser::create(newpath.chars()));
auto parent = TRY(parser.dirname());
// FIXME: Use AT_SYMLINK_FOLLOW.
auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false));
if (target->type() == VFS::InodeType::Directory) return err(EPERM);
auto parent_inode = TRY(current->resolve_atfile(newdirfd, parent, false, true));
if (target->fs() != parent_inode->fs()) return err(EXDEV);
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
auto child_name = TRY(parser.basename());
TRY(VFS::validate_filename(child_name.view()));
TRY(parent_inode->add_entry(target, child_name.chars()));
return 0;
}

View File

@ -12,7 +12,7 @@ Result<u64> sys_mkdir(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
auto inode = TRY(VFS::create_directory(path.chars(), current->auth, current->current_directory));
inode->chmod(mode & ~current->umask);
inode->chmod(mode);
inode->chown(current->auth.euid, current->auth.egid);
return 0;

36
kernel/src/sys/mknod.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "Log.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/makedev.h>
#include <luna/PathParser.h>
Result<u64> sys_mknod(Registers*, SyscallArgs args)
{
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
mode_t mode = (mode_t)args[1];
dev_t dev = (dev_t)args[2];
Thread* current = Scheduler::current();
u32 maj = luna_dev_major(dev);
u32 min = luna_dev_minor(dev);
auto parser = TRY(PathParser::create(path.chars()));
auto dirname = TRY(parser.dirname());
auto basename = TRY(parser.basename());
TRY(VFS::validate_filename(basename.view()));
auto parent = TRY(VFS::resolve_path(dirname.chars(), current->auth, current->current_directory));
if (!VFS::can_write(parent, current->auth)) return err(EACCES);
auto inode = TRY(parent->fs().create_device_inode(maj, min));
TRY(parent->add_entry(inode, basename.chars()));
TRY(inode->chmod(mode));
return 0;
}

View File

@ -1,34 +0,0 @@
#include "fs/VFS.h"
#include "fs/tmpfs/FileSystem.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
Result<u64> sys_mount(Registers*, SyscallArgs args)
{
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
auto* current = Scheduler::current();
if (current->auth.euid != 0) return err(EPERM);
// Right now we only support one file system.
if (fstype.view() != "tmpfs") return err(ENODEV);
auto fs = TRY(TmpFS::FileSystem::create());
TRY(VFS::mount(target.chars(), fs, current->auth, current->current_directory));
return 0;
}
Result<u64> sys_umount(Registers*, SyscallArgs args)
{
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
auto* current = Scheduler::current();
if (current->auth.euid != 0) return err(EPERM);
TRY(VFS::umount(target.chars(), current->auth, current->current_directory));
return 0;
}

View File

@ -23,22 +23,17 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
// Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR
if ((flags & O_RDWR) == 0) { return err(EINVAL); }
if (flags & O_TMPFILE)
{
if (!(flags & O_WRONLY)) return err(EINVAL);
if (flags & O_CREAT) return err(EINVAL);
}
if ((flags & O_DIRECTORY) & (flags & O_CREAT)) return err(EINVAL);
int error;
SharedPtr<VFS::Inode> parent_inode;
bool ok = current->resolve_atfile(dirfd, path, false, !(flags & O_NOFOLLOW), &parent_inode)
.try_set_value_or_error(inode, error);
bool ok = current->resolve_atfile(dirfd, path, false, &parent_inode).try_set_value_or_error(inode, error);
if (!ok)
{
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
{
inode = TRY(VFS::create_file(path.chars(), current->auth, parent_inode));
inode->chmod(mode & ~current->umask);
inode->chmod(mode);
inode->chown(current->auth.euid, current->auth.egid);
}
else
@ -52,32 +47,14 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
}
// This should only be possible if O_NOFOLLOW was in flags.
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
if (flags & O_TMPFILE)
{
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);
inode = TRY(inode->fs()->create_file_inode());
inode->chmod(mode);
inode->chown(current->auth.euid, current->auth.egid);
}
if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR);
if (inode->type() == VFS::InodeType::Directory)
{
if ((flags & O_WRONLY) || (flags & O_CREAT)) return err(EISDIR);
}
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
int fd = TRY(current->allocate_fd(0));
kinfoln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
inode->add_handle();
current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP };
return (u64)fd;
@ -94,8 +71,6 @@ Result<u64> sys_close(Registers*, SyscallArgs args)
if (!descriptor.has_value()) return err(EBADF);
descriptor->inode->remove_handle();
descriptor = {};
return 0;

View File

@ -1,41 +0,0 @@
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/pstat.h>
static void set_timespec(struct timespec& ts, u64 ticks)
{
ts.tv_sec = ticks / 1000;
ts.tv_nsec = (ticks % 1000) * 1000 * 1000;
}
Result<u64> sys_pstat(Registers*, SyscallArgs args)
{
pid_t pid = (pid_t)args[0];
struct process* ps = (struct process*)args[1];
// If pid == -1, return the PID of the last spawned thread.
if (pid == -1) return g_threads.expect_last()->id;
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
struct process proc;
proc.ps_pid = (pid_t)thread->id;
proc.ps_ppid = thread->parent ? (pid_t)thread->parent->id : 0;
proc.ps_uid = thread->auth.uid;
proc.ps_gid = thread->auth.gid;
proc.ps_euid = thread->auth.euid;
proc.ps_egid = thread->auth.egid;
proc.ps_state = (int)thread->state;
proc.ps_flags = thread->is_kernel ? PS_FLAG_KRNL : 0;
set_timespec(proc.ps_time, thread->user_ticks_self + thread->kernel_ticks_self);
set_timespec(proc.ps_ktime, thread->kernel_ticks_self);
set_timespec(proc.ps_utime, thread->kernel_ticks_children);
strlcpy(proc.ps_name, thread->name.chars(), sizeof(proc.ps_name));
strlcpy(proc.ps_cwd, thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars(),
sizeof(proc.ps_cwd));
if (!MemoryManager::copy_to_user_typed(ps, &proc)) return err(EFAULT);
return (u64)pid;
}

View File

@ -1,32 +0,0 @@
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/rusage.h>
static void ticks_to_rusage(struct rusage* ru, u64 sticks, u64 uticks)
{
ru->ru_stime.tv_sec = sticks / 1000;
ru->ru_stime.tv_usec = (sticks % 1000) * 1000;
ru->ru_utime.tv_sec = uticks / 1000;
ru->ru_utime.tv_usec = (uticks % 1000) * 1000;
}
Result<u64> sys_getrusage(Registers*, SyscallArgs args)
{
int who = (int)args[0];
struct rusage* ru = (struct rusage*)args[1];
auto* current = Scheduler::current();
struct rusage kru;
switch (who)
{
case RUSAGE_SELF: ticks_to_rusage(&kru, current->kernel_ticks_self, current->user_ticks_self); break;
case RUSAGE_CHILDREN: ticks_to_rusage(&kru, current->kernel_ticks_children, current->user_ticks_children); break;
default: return err(EINVAL);
}
if (!MemoryManager::copy_to_user_typed(ru, &kru)) return err(EFAULT);
return 0;
}

View File

@ -1,7 +1,6 @@
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/access.h>
#include <bits/atfile.h>
#include <bits/modes.h>
#include <bits/struct_stat.h>
@ -14,10 +13,7 @@ static mode_t make_mode(mode_t mode, VFS::InodeType type)
{
case VFS::InodeType::RegularFile: result |= S_IFREG; break;
case VFS::InodeType::Directory: result |= S_IFDIR; break;
case VFS::InodeType::CharacterDevice: result |= S_IFCHR; break;
case VFS::InodeType::BlockDevice: result |= S_IFBLK; break;
case VFS::InodeType::Symlink: result |= S_IFLNK; break;
case VFS::InodeType::FIFO: result |= S_IFIFO; break;
case VFS::InodeType::Device: result |= S_IFCHR; break;
default: break;
}
@ -33,50 +29,18 @@ Result<u64> sys_fstatat(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH));
stat kstat;
kstat.st_ino = inode->inode_number();
kstat.st_mode = make_mode(inode->mode(), inode->type());
kstat.st_nlink = inode->nlinks();
kstat.st_nlink = 1; // FIXME: Count hard links to files.
kstat.st_uid = inode->uid();
kstat.st_gid = inode->gid();
kstat.st_size = inode->size();
kstat.st_dev = inode->fs() ? inode->fs()->host_device_id() : 0;
kstat.st_rdev = inode->device_id();
if (!MemoryManager::copy_to_user_typed(st, &kstat)) return err(EFAULT);
return 0;
}
Result<u64> sys_faccessat(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
int amode = (int)args[2];
int flags = (int)args[3];
Credentials creds;
auto* current = Scheduler::current();
if (flags & AT_EACCESS) creds = current->auth;
else
{
auto auth = current->auth;
creds.euid = auth.uid;
creds.egid = auth.gid;
}
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
if ((amode & R_OK) && !VFS::can_read(inode, creds)) return err(EACCES);
if ((amode & W_OK) && !VFS::can_write(inode, creds)) return err(EACCES);
if ((amode & X_OK) && !VFS::can_execute(inode, creds)) return err(EACCES);
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
// otherwise.
return 0;
}

View File

@ -27,7 +27,7 @@ Result<u64> sys_uname(Registers*, SyscallArgs args)
// FIXME: Hardcode this at build time instead of in code (should be the short commit hash).
strncpy(result.version, "alpha", _UTSNAME_LENGTH);
strncpy(result.machine, CPU::platform_string().chars(), _UTSNAME_LENGTH);
strncpy(result.machine, CPU::platform_string(), _UTSNAME_LENGTH);
if (!MemoryManager::copy_to_user_typed(buf, &result)) return err(EFAULT);

View File

@ -17,44 +17,35 @@ Result<u64> sys_waitpid(Registers*, SyscallArgs args)
{
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
if (thread->parent && thread->parent != current) return err(ECHILD);
if (thread->parent_id != current->id) return err(ECHILD);
if (options & WNOHANG) return err(EAGAIN);
if (thread->state != ThreadState::Exited) kernel_wait(pid);
check(thread->state == ThreadState::Exited);
while (thread->state != ThreadState::Exited)
{
if (options & WNOHANG) return err(EAGAIN);
kernel_sleep(10);
}
}
else if (pid == -1)
{
if (!Scheduler::has_children(current)) return err(ECHILD);
if (!Scheduler::has_children((pid_t)current->id)) return err(ECHILD);
auto child = Scheduler::find_exited_child(current);
if (!child.has_value())
Option<Thread*> child;
while (child = Scheduler::find_exited_child((pid_t)current->id), !child.has_value())
{
if (options & WNOHANG) return err(EAGAIN);
kernel_wait(pid);
check(current->child_being_waited_for.value_or(-1) != -1);
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(*current->child_being_waited_for), ESRCH));
check(thread->state == ThreadState::Exited);
kernel_sleep(10);
}
else
thread = child.value();
thread = child.value();
}
else
return err(ENOTSUP);
current->child_being_waited_for = {};
int status = (int)thread->status;
u64 id = thread->id;
current->user_ticks_children += thread->user_ticks_self;
current->kernel_ticks_children += thread->kernel_ticks_self;
thread->state = ThreadState::Dying;
Scheduler::signal_reap_thread();
if (status_ptr)
if (!MemoryManager::copy_to_user_typed(status_ptr, &status)) return err(EFAULT);

View File

@ -11,8 +11,6 @@
static Thread g_idle;
static Thread* g_current = nullptr;
static Thread* g_init = nullptr;
static Thread* g_reap = nullptr;
static const usize TICKS_PER_TIMESLICE = 20;
@ -25,7 +23,7 @@ namespace Scheduler
g_idle.set_ip((u64)CPU::idle_loop);
g_idle.state = ThreadState::Idle;
g_idle.is_kernel = true;
g_idle.parent = nullptr;
g_idle.parent_id = 0;
g_idle.name = "[idle]";
g_idle.ticks_left = 1;
@ -54,22 +52,7 @@ namespace Scheduler
return &g_idle;
}
Thread* init_thread()
{
return g_init;
}
void set_reap_thread(Thread* thread)
{
g_reap = thread;
}
void signal_reap_thread()
{
if (g_reap) g_reap->wake_up();
}
Result<Thread*> new_kernel_thread_impl(Thread* thread, const char* name)
Result<void> new_kernel_thread_impl(Thread* thread, const char* name)
{
// If anything fails, make sure to clean up.
auto guard = make_scope_guard([&] { delete thread; });
@ -83,6 +66,8 @@ namespace Scheduler
thread->stack = thread_stack;
thread->parent_id = 0;
thread->name = name;
thread->is_kernel = true;
@ -91,14 +76,12 @@ namespace Scheduler
g_threads.append(thread);
thread->state = ThreadState::Runnable;
kinfoln("Created kernel thread: id %lu with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp());
return thread;
return {};
}
Result<Thread*> new_kernel_thread(u64 address, const char* name)
Result<void> new_kernel_thread(u64 address, const char* name)
{
Thread* const thread = TRY(new_thread());
thread->init_regs_kernel();
@ -107,7 +90,7 @@ namespace Scheduler
return new_kernel_thread_impl(thread, name);
}
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name)
Result<void> new_kernel_thread(void (*func)(void), const char* name)
{
Thread* const thread = TRY(new_thread());
thread->init_regs_kernel();
@ -116,7 +99,7 @@ namespace Scheduler
return new_kernel_thread_impl(thread, name);
}
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
{
Thread* const thread = TRY(new_thread());
thread->init_regs_kernel();
@ -126,14 +109,13 @@ namespace Scheduler
return new_kernel_thread_impl(thread, name);
}
Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name)
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name)
{
Thread* const thread = TRY(make<Thread>());
Thread* const thread = TRY(new_thread());
thread->state = ThreadState::None;
thread->is_kernel = false;
thread->id = 1;
thread->name = name;
thread->parent_id = 0;
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
Vector<String> args;
@ -157,9 +139,8 @@ namespace Scheduler
thread->sp(), thread->kernel_stack.top());
g_threads.append(thread);
g_init = thread;
return thread;
return {};
}
void add_thread(Thread* thread)
@ -182,11 +163,6 @@ namespace Scheduler
{
auto stack = thread->kernel_stack;
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
for (int i = 0; i < FD_MAX; i++)
{
if (thread->fd_table[i].has_value()) thread->fd_table[i]->inode->remove_handle();
}
}
if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory);
@ -261,19 +237,20 @@ namespace Scheduler
{
CPU::disable_interrupts();
if (is_in_kernel(regs)) g_current->kernel_ticks_self++;
g_current->ticks++;
if (is_in_kernel(regs)) g_current->ticks_in_kernel++;
else
g_current->user_ticks_self++;
g_current->ticks_in_user++;
g_current->ticks_left--;
for (auto* const thread : g_threads)
{
g_threads.for_each([](Thread* thread) {
if (thread->state == ThreadState::Sleeping)
{
if (--thread->sleep_ticks_left == 0) thread->wake_up();
if (--thread->sleep_ticks_left == 0) thread->state = ThreadState::Runnable;
}
}
});
if (!g_current->ticks_left) switch_task(regs);
}
@ -295,63 +272,34 @@ namespace Scheduler
Option<Thread*> find_by_pid(pid_t pid)
{
for (auto* const thread : g_threads)
{
if (thread->id == (u64)pid && thread->state != ThreadState::Dying) return thread;
}
Option<Thread*> result;
return {};
g_threads.for_each([&](Thread* thread) {
if (thread->id == (u64)pid && thread->state != ThreadState::Dying) result = thread;
});
return result;
}
bool has_children(Thread* thread)
bool has_children(pid_t pid)
{
bool result { false };
for_each_child(thread, [&](Thread*) {
result = true;
return false;
});
for_each_child(pid, [&](Thread*) { result = true; });
return result;
}
Option<Thread*> find_exited_child(Thread* thread)
Option<Thread*> find_exited_child(pid_t pid)
{
Option<Thread*> result;
for_each_child(thread, [&](Thread* child) {
if (!result.has_value() && child->state == ThreadState::Exited)
{
result = child;
return false;
}
return true;
for_each_child(pid, [&](Thread* child) {
if (!result.has_value() && child->state == ThreadState::Exited) result = child;
});
return result;
}
void dump_state()
{
CPU::disable_interrupts();
kdbgln("--- BEGIN SCHEDULER DUMP ---");
kdbgln("current at %p, id = %zu", g_current, g_current->id);
for (const auto* thread : g_threads)
{
kdbgln("%p %c [%-20s] %4zu, parent = (%-18p,%zu), state = %d, ticks: (k:%04zu,u:%04zu), status = "
"%d, cwd = %s",
thread, thread->is_kernel ? 'k' : 'u', thread->name.chars(), thread->id, thread->parent,
thread->parent ? thread->parent->id : 0, (int)thread->state, thread->kernel_ticks_self,
thread->user_ticks_self, thread->status,
thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars());
}
kdbgln("--- END SCHEDULER DUMP ---");
CPU::enable_interrupts();
}
}
void kernel_sleep(u64 ms)
@ -361,23 +309,9 @@ void kernel_sleep(u64 ms)
kernel_yield();
}
void kernel_wait(pid_t pid)
{
g_current->child_being_waited_for = pid;
g_current->state = ThreadState::Waiting;
kernel_yield();
}
void kernel_wait_for_event()
{
g_current->state = ThreadState::Waiting;
kernel_yield();
}
[[noreturn]] void kernel_exit()
{
g_current->state = ThreadState::Dying;
Scheduler::signal_reap_thread();
kernel_yield();
unreachable();
}

View File

@ -8,16 +8,12 @@ namespace Scheduler
Thread* current();
Thread* idle();
Thread* init_thread();
void set_reap_thread(Thread*);
void signal_reap_thread();
Result<void> new_kernel_thread(u64 address, const char* name);
Result<void> new_kernel_thread(void (*func)(void), const char* name);
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
Result<Thread*> new_kernel_thread(u64 address, const char* name);
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name);
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);
void add_thread(Thread* thread);
@ -33,29 +29,18 @@ namespace Scheduler
Option<Thread*> find_by_pid(pid_t pid);
template <typename Callback> void for_each_child(Thread* thread, Callback callback)
template <typename Callback> void for_each_child(pid_t pid, Callback callback)
{
for (Thread* current = thread; current; current = g_threads.next(current).value_or(nullptr))
{
if (current->parent == thread)
{
bool should_continue = callback(current);
if (!should_continue) return;
}
}
g_threads.for_each([&](Thread* thread) {
if (thread->parent_id == (u64)pid) callback(thread);
});
}
void dump_state();
bool has_children(pid_t pid);
bool has_children(Thread* thread);
Option<Thread*> find_exited_child(Thread* thread);
Option<Thread*> find_exited_child(pid_t pid);
}
extern "C" void kernel_yield();
void kernel_wait(pid_t pid);
void kernel_sleep(u64 ms);
[[noreturn]] void kernel_exit();
// Freezes the current thread until someone else calls wake_up() on this thread.
void kernel_wait_for_event();

View File

@ -12,7 +12,7 @@ LinkedList<Thread> g_threads;
void Thread::init()
{
g_next_id = 2;
g_next_id = 1;
}
Result<Thread*> new_thread()
@ -49,15 +49,13 @@ Result<FileDescriptor*> Thread::resolve_fd(int fd)
}
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
SharedPtr<VFS::Inode>* parent_inode)
{
if (parent_inode) *parent_inode = this->current_directory;
if (PathParser::is_absolute(path.view()))
return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink);
if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this->auth);
if (dirfd == AT_FDCWD)
return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink);
if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this->auth, this->current_directory);
auto descriptor = TRY(resolve_fd(dirfd));
@ -65,7 +63,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
if (path.is_empty() && allow_empty_path) return descriptor->inode;
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode);
}
bool FileDescriptor::should_append()

View File

@ -18,11 +18,9 @@
enum class ThreadState
{
None,
Idle,
Runnable,
Sleeping,
Waiting,
Exited,
Dying
};
@ -56,13 +54,13 @@ struct Thread : public LinkedListNode<Thread>
Registers regs;
u64 id;
u64 parent_id;
Credentials auth;
u64 user_ticks_self = 0;
u64 kernel_ticks_self = 0;
u64 user_ticks_children = 0;
u64 kernel_ticks_children = 0;
u64 ticks = 0;
u64 ticks_in_user = 0;
u64 ticks_in_kernel = 0;
u64 ticks_left;
u64 sleep_ticks_left;
@ -76,7 +74,6 @@ struct Thread : public LinkedListNode<Thread>
Result<int> allocate_fd(int min);
Result<FileDescriptor*> resolve_fd(int fd);
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
bool follow_last_symlink,
SharedPtr<VFS::Inode>* parent_inode = nullptr);
FPData fp_data;
@ -87,16 +84,11 @@ struct Thread : public LinkedListNode<Thread>
u8 status { 0 };
mode_t umask { 0 };
StaticString<128> name;
String current_directory_path = {};
SharedPtr<VFS::Inode> current_directory = {};
Thread* parent { nullptr };
Option<pid_t> child_being_waited_for = {};
PageDirectory* directory;
bool is_idle()
@ -104,11 +96,6 @@ struct Thread : public LinkedListNode<Thread>
return state == ThreadState::Idle;
}
void wake_up()
{
state = ThreadState::Runnable;
}
void init_regs_kernel();
void init_regs_user();

View File

@ -3,15 +3,11 @@
#include "thread/Thread.h"
#include <luna/CString.h>
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
static constexpr usize DEFAULT_USER_STACK_SIZE = DEFAULT_USER_STACK_PAGES * ARCH_PAGE_SIZE;
static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
{
const u64 THREAD_STACK_BASE = 0x10000;
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
MMU::ReadWrite | MMU::NoExecute | MMU::User));
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, 4, MMU::ReadWrite | MMU::NoExecute | MMU::User));
auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); });
@ -19,7 +15,7 @@ static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
guard.deactivate();
user_stack = { THREAD_STACK_BASE, DEFAULT_USER_STACK_SIZE };
user_stack = { THREAD_STACK_BASE, 4 * ARCH_PAGE_SIZE };
kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
return {};

View File

@ -2,7 +2,6 @@
#include "boot/bootboot.h"
#include "video/Framebuffer.h"
#include <luna/CString.h>
#include <luna/CType.h>
#include <luna/Format.h>
#include <luna/Result.h>
#include <luna/ScopeGuard.h>
@ -110,7 +109,6 @@ namespace TextConsole
}
break;
default: {
if (_iscntrl(c)) return;
putwchar_at(c, g_x_position, g_y_position);
next_char();
if (at_end_of_screen())

View File

@ -18,15 +18,11 @@ set(SOURCES
src/setjmp.cpp
src/env.cpp
src/pwd.cpp
src/grp.cpp
src/sys/stat.cpp
src/sys/mman.cpp
src/sys/wait.cpp
src/sys/ioctl.cpp
src/sys/utsname.cpp
src/sys/mount.cpp
src/sys/pstat.cpp
src/sys/resource.cpp
)
if(${LUNA_ARCH} STREQUAL "x86_64")

View File

@ -1,8 +0,0 @@
/* alloca.h: Stack memory allocation. */
#ifndef _ALLOCA_H
#define _ALLOCA_H
#define alloca __builtin_alloca
#endif

View File

@ -1,12 +0,0 @@
/* bits/access.h: Definitions for the access() function. */
#ifndef _BITS_ACCESS_H
#define _BITS_ACCESS_H
#define R_OK 1
#define W_OK 2
#define X_OK 4
#define F_OK 0
#endif

View File

@ -6,10 +6,7 @@
#define AT_FDCWD -100
#define AT_EMPTY_PATH 1
#define AT_SYMLINK_NOFOLLOW 2
#define AT_REMOVEDIR 1
#define AT_EACCESS 1
#endif

View File

@ -3,7 +3,7 @@
#ifndef _BITS_ATTRS_H
#define _BITS_ATTRS_H
#if !defined(_STDLIB_H) && !defined(_STRING_H) && !defined(_ASSERT_H) && !defined(_SETJMP_H) && !defined(_SYS_TIME_H)
#if !defined(_STDLIB_H) && !defined(_STRING_H) && !defined(_ASSERT_H) && !defined(_SETJMP_H)
#error "Never include bits/attrs.h directly; use one of the standard library headers."
#endif

View File

@ -3,7 +3,7 @@
#ifndef _BITS_FIXED_SIZE_TYPES_H
#define _BITS_FIXED_SIZE_TYPES_H
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H)
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H)
#error "Never include bits/fixed-size-types.h, use the standard <stdint.h> header instead."
#endif

View File

@ -5,20 +5,12 @@
#define S_IFMT 070000
#define S_IFREG 000000
#define S_IFLNK 010000
#define S_IFIFO 020000
#define S_IFBLK 030000
#define S_IFDIR 040000
#define S_IFCHR 050000
#define __CHECK_TYPE(mode, type) (((mode)&S_IFMT) == type)
#define S_ISREG(mode) __CHECK_TYPE(mode, S_IFREG)
#define S_ISDIR(mode) __CHECK_TYPE(mode, S_IFDIR)
#define S_ISCHR(mode) __CHECK_TYPE(mode, S_IFCHR)
#define S_ISBLK(mode) __CHECK_TYPE(mode, S_IFBLK)
#define S_ISLNK(mode) __CHECK_TYPE(mode, S_IFLNK)
#define S_ISFIFO(mode) __CHECK_TYPE(mode, S_IFIFO)
#define S_ISREG(mode) ((mode)&S_IFMT == S_IFREG)
#define S_ISDIR(mode) ((mode)&S_IFMT == S_IFDIR)
#define S_ISCHR(mode) ((mode)&S_IFMT == S_IFCHR)
#define S_IRWXU 0700
#define S_IRUSR 0400

View File

@ -13,8 +13,6 @@
#define O_NONBLOCK 64
#define O_CLOEXEC 128
#define O_DIRECTORY 256
#define O_TMPFILE 512
#define O_NOFOLLOW 1024
#define O_ACCMODE O_RDWR

View File

@ -1,34 +0,0 @@
/* bits/pstat.h: The process structure and flags for the pstat() system call. */
#ifndef _BITS_PSTAT_H
#define _BITS_PSTAT_H
#include <bits/timespec.h>
#include <sys/types.h>
#define PS_IDLE 1
#define PS_RUNNABLE 2
#define PS_SLEEPING 3
#define PS_WAITING 4
#define PS_ENDED 5
#define PS_FLAG_KRNL 1
struct process
{
pid_t ps_pid;
pid_t ps_ppid;
uid_t ps_uid;
uid_t ps_euid;
gid_t ps_gid;
gid_t ps_egid;
int ps_state;
int ps_flags;
struct timespec ps_time;
struct timespec ps_ktime;
struct timespec ps_utime;
char ps_name[129];
char ps_cwd[256];
};
#endif

View File

@ -1,17 +0,0 @@
/* bits/rusage.h: The rusage structure. */
#ifndef _BITS_RUSAGE_H
#define _BITS_RUSAGE_H
#include <bits/timespec.h>
struct rusage
{
struct timeval ru_utime;
struct timeval ru_stime;
};
#define RUSAGE_SELF 0
#define RUSAGE_CHILDREN 1
#endif

View File

@ -10,15 +10,9 @@ struct stat
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
dev_t st_dev;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
dev_t st_rdev;
// FIXME: Actually fill these fields in.
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
};
#endif

View File

@ -1,4 +1,4 @@
/* bits/timespec.h: The timespec and timeval structures. */
/* bits/timespec.h: The timespec structure. */
#ifndef _BITS_TIMESPEC_H
#define _BITS_TIMESPEC_H
@ -11,10 +11,4 @@ struct timespec
long tv_nsec;
};
struct timeval
{
time_t tv_sec;
suseconds_t tv_usec;
};
#endif

View File

@ -22,11 +22,8 @@ extern "C"
#endif
DIR* opendir(const char* path);
DIR* fdopendir(int fd);
struct dirent* readdir(DIR* stream);
int closedir(DIR* stream);
int dirfd(DIR* stream);
void rewinddir(DIR* stream);
#ifdef __cplusplus
}

View File

@ -16,9 +16,6 @@ extern "C"
/* Open a file path and return a file descriptor to it. */
int open(const char* path, int flags, ...);
/* Open a file path relative to a file descriptor and return a file descriptor to it. */
int openat(int dirfd, const char* path, int flags, ...);
/* Create a file and return a file descriptor to it. */
int creat(const char* path, mode_t mode);

Some files were not shown because too many files have changed in this diff Show More