Compare commits

..

No commits in common. "e029679fba03224b9dd55f62ba6604622d7e5f4b" and "eeb9e16a74a66bfbfc5aa7507cd97fdfa8cc1880" have entirely different histories.

262 changed files with 1823 additions and 8616 deletions

3
.gitignore vendored
View File

@ -5,8 +5,5 @@ initrd/boot/moon
env-local.sh
initrd/bin/**
base/usr/**
base/etc/skel/LICENSE
.fakeroot
kernel/config.cmake
ports/out/
ports/temp/

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.5.0)
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.4.0)
set(LUNA_RELEASE_NAME "Mercury") # Name for alpha releases
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
@ -33,7 +33,7 @@ set(COMMON_FLAGS -Wall -Wextra -Werror -Wvla
-Wdisabled-optimization -Wformat=2 -Winit-self
-Wmissing-include-dirs -Wswitch-default -Wcast-qual
-Wundef -Wcast-align -Wwrite-strings -Wlogical-op
-Wredundant-decls -Wshadow -Wconversion -Wbidi-chars=any
-Wredundant-decls -Wshadow -Wconversion
-fno-asynchronous-unwind-tables -fno-omit-frame-pointer
-std=c++20 -fno-rtti -fno-exceptions)

View File

@ -1,5 +1,5 @@
# Luna
A simple 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-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)
## Another UNIX clone?
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
@ -8,18 +8,15 @@ A simple 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/).
- [Virtual file system](kernel/src/fs/) with a simple [tmpfs](kernel/src/fs/tmpfs/) and read-only [ext2](kernel/src/fs/ext2/) support.
- Can [load ELF programs](kernel/src/binfmt/ELF.cpp), [shebang scripts](kernel/src/binfmt/Script.cpp) or [arbitrary binary formats](kernel/src/binfmt/BinaryFormat.h) (registered through kernel modules, which are not supported yet =D).
- Can [load ELF programs](kernel/src/thread/ELF.cpp) from the file system as userspace tasks.
- Boots from an [ext2](apps/preinit.cpp) root filesystem (a bit slow for now).
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be almost POSIX-compatible.
- Support for [several third-party programs](ports/), including the [GNU binutils](ports/binutils/PACKAGE) suite of utilities.
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be mostly POSIX-compatible.
- POSIX [signal](libc/src/signal.cpp) support.
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
- Everything is [UTF-8](libluna/include/luna/Utf8.h).
- [UNIX local domain sockets](kernel/src/net/UnixSocket.cpp), allowing for local IPC.
- [POSIX shared memory](libc/include/sys/mman.h) support.
- Designed around [UTF-8](libluna/include/luna/Utf8.h).
- Environment-agnostic [utility library](libluna/), which can be used in both kernel and userspace.
- Return-oriented [error propagation](libluna/include/luna/Result.h), inspired by Rust and SerenityOS. No exceptions here :).
- An extensive set of [standard Unix utilities](apps/), from [ls](apps/ls.cpp) to [uname](apps/uname.cpp) to [base64](apps/base64.cpp). Written in modern C++ and very small amounts of code, using Luna's practical [OS library](libos/).
- Return-oriented [error propagation](libluna/include/luna/Result.h), inspired by Rust and SerenityOS.
- Build system uses [CMake](CMakeLists.txt).
## Setup
@ -53,7 +50,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?
Yes! A ports system is in place, and you can use the build scripts to add some ports to your image. More information in the [Ports](ports/README.md) page.
There is no infrastructure for porting third-party software nor there are any patches in the repo for now, but some third-party programs can run on Luna, including [my own library](https://git.cloudapio.eu/apio/minitar).
## License
Luna is open-source and free software under the [BSD-2-Clause License](LICENSE).
Luna is open-source and free software under the [BSD-2 License](LICENSE).

View File

@ -28,6 +28,8 @@ 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(signal-test.cpp signal-test)
luna_app(mount.cpp mount)
luna_app(umount.cpp umount)
luna_app(ps.cpp ps)
@ -35,8 +37,8 @@ luna_app(time.cpp time)
luna_app(ln.cpp ln)
luna_app(mktemp.cpp mktemp)
luna_app(sysfuzz.cpp sysfuzz)
luna_app(pivot_root.cpp pivot_root)
luna_app(cp.cpp cp)
luna_app(kill.cpp kill)
luna_app(gol.cpp gol)
luna_app(touch.cpp touch)
luna_app(free.cpp free)
luna_app(buffer-test.cpp buffer-test)

27
apps/buffer-test.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_buf.mode);
fputs("hi!", stdout);
sleep(3);
putchar('\n');
fprintf(stderr, "Incomplete line should have been written.\n");
FILE* f = fopen("/dev/console", "w+");
assert(f);
assert(setvbuf(f, NULL, _IOFBF, 0) == 0);
fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_buf.mode);
fputs("Hello world!\nHow are you doing!\nThis is a test for many lines of buffering.\n", f);
sleep(3);
fflush(f);
fprintf(stderr, "Long text should have been written.\n");
fclose(f);
}

View File

@ -1,8 +1,6 @@
#include <luna/String.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/FileSystem.h>
#include <os/Prompt.h>
using os::File;
@ -16,20 +14,10 @@ Result<int> luna_main(int argc, char** argv)
parser.add_positional_argument(pathname, "path"_sv, true);
parser.parse(argc, argv);
if (os::FileSystem::exists(pathname))
{
String prompt = TRY(String::format("File %s already exists. Overwrite?"_sv, pathname.chars()));
bool overwrite = os::conditional_prompt(prompt.chars(), os::DefaultNo);
if (!overwrite) return 0;
}
auto file = TRY(File::open_or_create(pathname, File::WriteOnly));
auto input = File::standard_input();
os::println("- Editing %s. Press Enter to start a new line, or Ctrl+D at the start of a line to save and exit. -",
pathname.chars());
while (1)
{
String line = TRY(input->read_line());

View File

@ -1,50 +0,0 @@
#include <luna/Units.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <sys/memstat.h>
Result<int> luna_main(int argc, char** argv)
{
bool reserved { false };
bool human_readable { false };
os::ArgumentParser parser;
parser.add_description("Query the amount of memory available and used in the system.");
parser.add_system_program_info("free"_sv);
parser.add_switch_argument(reserved, 'r', "show-reserved"_sv, "show reserved system memory"_sv);
parser.add_switch_argument(human_readable, 'h', "human-readable"_sv, "show output in human-readable units"_sv);
parser.parse(argc, argv);
struct membuf buf;
memstat(&buf);
if (human_readable)
{
auto total = TRY(to_dynamic_unit(buf.mem_total, 10, false, Unit::Binary, true));
auto free = TRY(to_dynamic_unit(buf.mem_free, 10, false, Unit::Binary, true));
auto used = TRY(to_dynamic_unit(buf.mem_used, 10, false, Unit::Binary, true));
os::println("%s total memory, out of which:", total.chars());
os::println("%s used", used.chars());
os::println("%s free", free.chars());
}
else
{
os::println("%lu total memory, out of which:", buf.mem_total);
os::println("%lu used", buf.mem_used);
os::println("%lu free", buf.mem_free);
}
if (reserved)
{
if (human_readable)
{
auto reserved_string = TRY(to_dynamic_unit(buf.mem_reserved, 10, false, Unit::Binary, true));
os::println("%s reserved", reserved_string.chars());
}
else { os::println("%lu reserved", buf.mem_reserved); }
}
return 0;
}

View File

@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
@ -25,7 +24,6 @@ static int g_fb_height;
static int g_fd;
static Cell* g_cells;
static char* g_fb;
static Result<void> fill_cells()
{
@ -49,29 +47,27 @@ static Cell& find_cell(int row, int column)
static constexpr int BYTES_PER_PIXEL = 4;
static char* g_buf;
static void draw_cells()
{
lseek(g_fd, 0, SEEK_SET);
const int CELL_WIDTH = g_fb_width / g_num_columns;
const int CELL_HEIGHT = g_fb_height / g_num_rows;
for (int i = 0; i < g_num_rows; i++)
{
memset(g_buf, 0, g_fb_width * BYTES_PER_PIXEL);
for (int j = 0; j < g_num_columns; j++)
{
char* buf = g_fb + (i * g_fb_width * CELL_HEIGHT * BYTES_PER_PIXEL);
auto& cell = find_cell(i, j);
u8 color = cell.state ? 0xff : 0x00;
for (int k = 0; k < CELL_HEIGHT; k++)
{
memset(buf + (j * CELL_WIDTH * BYTES_PER_PIXEL), color, CELL_WIDTH * BYTES_PER_PIXEL);
buf += g_fb_width * BYTES_PER_PIXEL;
}
if (cell.state) memset(g_buf + (j * CELL_WIDTH * BYTES_PER_PIXEL), 0xff, CELL_WIDTH * BYTES_PER_PIXEL);
}
}
msync(g_fb, g_fb_height * g_fb_width * BYTES_PER_PIXEL, MS_SYNC);
for (int j = 0; j < CELL_HEIGHT; j++) { write(g_fd, g_buf, g_fb_width * BYTES_PER_PIXEL); }
}
}
static int find_neighbors(int row, int column)
@ -141,7 +137,7 @@ Result<int> luna_main(int argc, char** argv)
else
srand((unsigned)time(NULL));
g_fd = open("/dev/fb0", O_RDWR);
g_fd = open("/dev/fb0", O_WRONLY);
if (g_fd < 0)
{
perror("gol: cannot open framebuffer for writing");
@ -153,13 +149,7 @@ Result<int> luna_main(int argc, char** argv)
TRY(fill_cells());
g_fb =
(char*)mmap(nullptr, g_fb_height * g_fb_width * BYTES_PER_PIXEL, PROT_READ | PROT_WRITE, MAP_SHARED, g_fd, 0);
if (g_fb == MAP_FAILED)
{
perror("gol: cannot map framebuffer into memory");
return 1;
}
g_buf = (char*)TRY(calloc_impl(g_fb_width, BYTES_PER_PIXEL));
draw_cells();
@ -172,8 +162,5 @@ Result<int> luna_main(int argc, char** argv)
usleep(delay_at_end * 1000);
munmap(g_fb, g_fb_height * g_fb_width * BYTES_PER_PIXEL);
free(g_cells);
return 0;
}

View File

@ -3,14 +3,10 @@
#include <luna/PathParser.h>
#include <luna/Sort.h>
#include <luna/String.h>
#include <luna/Utf8.h>
#include <luna/Vector.h>
#include <os/ArgumentParser.h>
#include <os/Directory.h>
#include <os/File.h>
#include <os/Process.h>
#include <os/Security.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@ -21,10 +17,20 @@
#include <sys/sysmacros.h>
#include <unistd.h>
static bool g_is_system = false;
FILE* g_init_log = nullptr;
// Request a successful exit from the system (for tests)
void sigterm_handler(int)
{
_exit(0);
}
// Request a failure exit from the system (for tests)
void sigquit_handler(int)
{
_exit(1);
}
struct Service
{
String name;
@ -34,8 +40,6 @@ struct Service
String standard_output;
String standard_error;
String standard_input;
Option<uid_t> user {};
Option<gid_t> group {};
bool wait { false };
Option<pid_t> pid {};
};
@ -52,20 +56,6 @@ static void do_log(const char* format, ...)
va_end(ap);
}
// Request a successful exit from the system (for tests)
void sigterm_handler(int)
{
do_log("[init] successful exit requested, complying\n");
exit(0);
}
// Request a failure exit from the system (for tests)
void sigquit_handler(int)
{
do_log("[init] failure exit requested, complying\n");
exit(1);
}
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
SharedPtr<os::File> input)
{
@ -75,12 +65,6 @@ static Result<void> service_child(const Service& service, SharedPtr<os::File> ou
if (error) dup2(error->fd(), STDERR_FILENO);
if (input) dup2(input->fd(), STDIN_FILENO);
if (service.user.has_value())
{
setgid(service.group.value());
setuid(service.user.value());
}
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
else
{
@ -191,10 +175,28 @@ static Result<void> load_service(const os::Path& path)
if (parts[0].view() == "Command")
{
if (!service.command.is_empty())
{
do_log("[init] 'Command' cannot be specified after 'Script' has already been set! (%s)\n",
line.chars());
return {};
}
service.command = move(parts[1]);
continue;
}
if (parts[0].view() == "Script")
{
if (!service.command.is_empty())
{
do_log("[init] 'Script' cannot be specified after 'Command' has already been set! (%s)\n",
line.chars());
return {};
}
service.command = TRY(String::format("/bin/sh -- %s"_sv, parts[1].chars()));
continue;
}
if (parts[0].view() == "Restart")
{
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
@ -230,15 +232,6 @@ static Result<void> load_service(const os::Path& path)
continue;
}
if (g_is_system && parts[0].view() == "User")
{
auto* pw = getpwnam(parts[1].chars());
if (!pw) continue;
service.user = pw->pw_uid;
service.group = pw->pw_gid;
continue;
}
if (parts[0].view() == "Wait")
{
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
@ -261,7 +254,7 @@ static Result<void> load_service(const os::Path& path)
if (service.command.is_empty())
{
do_log("[init] service file is missing 'Command' entry, aborting!\n");
do_log("[init] service file is missing 'Command' or 'Script' entry, aborting!\n");
return {};
}
@ -272,9 +265,9 @@ static Result<void> load_service(const os::Path& path)
return {};
}
static Result<void> load_services(StringView path)
static Result<void> load_services()
{
auto dir = TRY(os::Directory::open(path));
auto dir = TRY(os::Directory::open("/etc/init"));
auto services = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
sort(services.begin(), services.end(), String::compare);
@ -284,9 +277,9 @@ static Result<void> load_services(StringView path)
return {};
}
static Result<void> start_services(StringView path)
static Result<void> start_services()
{
TRY(load_services(path));
TRY(load_services());
for (auto& service : g_services)
{
do_log("[init] starting service %s\n", service.name.chars());
@ -303,19 +296,6 @@ static Result<void> set_hostname()
auto hostname = TRY(file->read_line());
hostname.trim("\n");
if (hostname.is_empty())
{
do_log("[init] /etc/hostname is empty or invalid, keeping the default hostname\n");
return {};
}
Utf8StringDecoder decoder(hostname.chars());
if (decoder.code_points().has_error())
{
do_log("[init] /etc/hostname is not valid UTF-8, keeping the default hostname\n");
return {};
}
if (sethostname(hostname.chars(), hostname.length()) < 0) return {};
do_log("[init] successfully set system hostname to '%s'\n", hostname.chars());
@ -330,16 +310,7 @@ static void mount_tmpfs()
if (chmod("/tmp", 01777) < 0) exit(255);
}
static void mount_shmfs()
{
if (mkdir("/dev/shm", 0755) < 0) exit(255);
if (mount("/dev/shm", "tmpfs", "tmpfs") < 0) exit(255);
if (chmod("/dev/shm", 01777) < 0) exit(255);
}
Result<int> sysinit(StringView path)
int main()
{
if (getpid() != 1)
{
@ -347,35 +318,25 @@ Result<int> sysinit(StringView path)
return 1;
}
g_is_system = true;
// 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");
TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr));
mount_tmpfs();
mount_shmfs();
umask(022);
g_init_log = fopen("/dev/uart0", "w");
check(g_init_log);
setlinebuf(g_init_log);
g_init_log = fopen("/dev/uart0", "w+");
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
set_hostname();
if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGTERM\n");
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT\n");
if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGTERM");
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT");
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
if (path.is_empty()) path = "/etc/init";
start_services(path);
start_services();
while (1)
{
@ -410,70 +371,3 @@ Result<int> sysinit(StringView path)
}
}
}
Result<int> user_init(StringView path)
{
setpgid(0, 0);
g_init_log = fopen("/dev/uart0", "w");
check(g_init_log);
setlinebuf(g_init_log);
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec", nullptr));
if (path.is_empty()) path = "/etc/user";
start_services(path);
TRY(os::Security::pledge("stdio rpath wpath proc exec", nullptr));
while (1)
{
int status;
auto rc = os::Process::wait(os::Process::ANY_CHILD, &status);
if (rc.has_error()) continue;
pid_t child = rc.release_value();
for (auto& service : g_services)
{
if (service.pid.has_value() && service.pid.value() == child)
{
if (WIFEXITED(status))
{
do_log("[init] service %s exited with status %d\n", service.name.chars(), WEXITSTATUS(status));
}
else
{
do_log("[init] service %s was terminated by signal %d\n", service.name.chars(), WTERMSIG(status));
}
if (service.restart)
{
do_log("[init] restarting service %s\n", service.name.chars());
start_service(service);
}
break;
}
}
}
}
Result<int> luna_main(int argc, char** argv)
{
bool user;
StringView service_path;
os::ArgumentParser parser;
parser.add_description("The init system for Luna.");
parser.add_system_program_info("init"_sv);
parser.add_switch_argument(user, 'u', "user"_sv, "initialize a user session instead of the system");
parser.add_value_argument(service_path, 's', "service-path"_sv,
"change the default service path (/etc/init or /etc/user)");
parser.parse(argc, argv);
if (user) return user_init(service_path);
return sysinit(service_path);
}

45
apps/ipc-test.cpp Normal file
View File

@ -0,0 +1,45 @@
#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

@ -26,12 +26,11 @@ Result<int> luna_main(int argc, char** argv)
String name;
setpgid(0, 0);
signal(SIGTTOU, SIG_IGN);
if (isatty(STDIN_FILENO)) tcsetpgrp(STDIN_FILENO, getpgid(0));
if (username.is_empty())
{
signal(SIGTTOU, SIG_IGN);
if (isatty(STDIN_FILENO)) tcsetpgrp(STDIN_FILENO, getpgid(0));
auto input = os::File::standard_input();
@ -45,7 +44,7 @@ Result<int> luna_main(int argc, char** argv)
username = name.view();
}
execl("/usr/bin/su", "login", "-lp", "--", username.chars(), nullptr);
execl("/bin/su", "login", "-lp", "--", username.chars(), nullptr);
perror("su");
return 1;

View File

@ -8,7 +8,6 @@
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <pwd.h>
#include <unistd.h>
void find_user_and_group(struct stat& st, StringView& owner, StringView& group)
{
@ -48,46 +47,18 @@ int sort_reverse(const os::Directory::Entry* a, const os::Directory::Entry* b)
return 0;
}
#define RESET_COLORS "\x1b[m"
#define SYMLINK_COLOR "\x1b[36m"
#define FILE_COLOR "\x1b[1;32m"
#define DIR_COLOR "\x1b[1;34m"
#define SOCKET_COLOR "\x1b[33m"
#define SPECIAL_COLOR "\x1b[35m"
#define STICKY_COLOR "\x1b[30;1;42m"
#define SETUID_COLOR "\x1b[30;1;41m"
#define EXEC_COLOR "\x1b[1;31m"
static const char* file_type_color(const os::Directory::Entry& entry)
{
if (entry.mode & S_ISVTX) return STICKY_COLOR;
if (entry.mode & S_ISUID || entry.mode & S_ISGID) return SETUID_COLOR;
switch (entry.mode & S_IFMT)
{
case S_IFREG: return entry.mode & S_IXUSR ? EXEC_COLOR : FILE_COLOR;
case S_IFDIR: return DIR_COLOR;
case S_IFLNK: return SYMLINK_COLOR;
case S_IFSOCK: return SOCKET_COLOR;
default: return SPECIAL_COLOR;
}
}
static Result<String> entry_join(const Vector<os::Directory::Entry>& vec, StringView delim, bool colors)
static Result<String> entry_join(const Vector<os::Directory::Entry>& vec, StringView delim)
{
if (vec.size() == 0) return String {};
if (vec.size() == 1) return vec[0].name.clone();
StringBuilder sb;
if (colors) TRY(sb.add(StringView { file_type_color(vec[0]) }));
TRY(sb.add(vec[0].name));
if (colors) TRY(sb.add(StringView { RESET_COLORS }));
for (usize i = 1; i < vec.size(); i++)
{
TRY(sb.add(delim));
if (colors) TRY(sb.add(StringView { file_type_color(vec[i]) }));
TRY(sb.add(vec[i].name));
if (colors) TRY(sb.add(StringView { RESET_COLORS }));
}
return sb.string();
@ -104,7 +75,6 @@ Result<int> luna_main(int argc, char** argv)
bool follow_symlink_args { false };
bool one_per_line { false };
bool list_directories { false };
bool no_colors { false };
StringView sort_type { "name" };
@ -128,8 +98,6 @@ Result<int> luna_main(int argc, char** argv)
parser.add_switch_argument(list_directories, 'd', "directory"_sv, "list directories instead of their contents"_sv);
parser.add_value_argument(sort_type, ' ', "sort"_sv, "sort by name, size or time"_sv);
parser.add_switch_argument(reverse_sort, 'r', "reverse"_sv, "reverse order while sorting"_sv);
parser.add_switch_argument(no_colors, ' ', "no-colors"_sv,
"disable coloring of output (defaults to true when not in a TTY)"_sv);
parser.parse(argc, argv);
Vector<os::Directory::Entry> files;
@ -186,13 +154,11 @@ Result<int> luna_main(int argc, char** argv)
if (!long_list)
{
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv, !no_colors && isatty(STDIN_FILENO)));
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv));
if (!list.is_empty()) os::println("%s", list.chars());
}
else
{
bool colors = !no_colors && isatty(STDIN_FILENO);
for (const auto& file : files)
{
struct stat st;
@ -210,32 +176,14 @@ Result<int> luna_main(int argc, char** argv)
if (!human_readable && !si)
{
if (colors)
{
os::println("%s %u %4s %4s %10lu %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
formatted_mode, st.st_nlink, owner.chars(), group.chars(), st.st_size,
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
if (colors)
{
os::println("%s %u %4s %4s %6s %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
formatted_mode, st.st_nlink, owner.chars(), group.chars(), size.chars(),
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
}
}

19
apps/pivot_root.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <os/ArgumentParser.h>
#include <sys/syscall.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv)
{
StringView new_root;
StringView put_old;
os::ArgumentParser parser;
parser.add_description("Move the current root directory to another directory and replace it with another mount.");
parser.add_system_program_info("pivot_root"_sv);
parser.add_positional_argument(new_root, "new_root", true);
parser.add_positional_argument(put_old, "put_old", true);
parser.parse(argc, argv);
long rc = syscall(SYS_pivot_root, new_root.chars(), put_old.chars());
return Result<int>::from_syscall(rc);
}

View File

@ -72,9 +72,13 @@ int main()
// Now, mount the /dev file system on the new root.
mount_devfs();
char* argv[] = { "/usr/bin/init", nullptr };
/*setenv("PATH", "/bin:/usr/bin", 1);
char* argv[] = { "init", nullptr };
char* envp[] = { nullptr };
execve(argv[0], argv, envp);
execvpe(argv[0], argv, envp);*/
char* argv[] = { "/usr/bin/init", nullptr };
execv(argv[0], argv);
fail_errno("Failed to execute init");

View File

@ -1,35 +1,10 @@
#include <os/ArgumentParser.h>
#include <os/Directory.h>
#include <os/File.h>
#include <os/FileSystem.h>
Result<void> remove_wrapper(const os::Path& path, bool verbose)
{
TRY(os::FileSystem::remove(path));
if (verbose) os::println("removed '%s'", path.name().chars());
return {};
}
Result<void> remove_tree(const os::Path& path, bool verbose)
{
auto rc = remove_wrapper(path, verbose);
if (!rc.has_error()) return {};
if (rc.error() != ENOTEMPTY) return rc.release_error();
auto dir = TRY(os::Directory::open(path));
Vector<String> entries = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
for (const auto& entry : entries) { TRY(remove_tree({ dir->fd(), entry.view() }, verbose)); }
return remove_wrapper(path, verbose);
}
Result<int> luna_main(int argc, char** argv)
{
StringView path;
bool recursive;
bool verbose;
os::ArgumentParser parser;
parser.add_description("Remove a path from the file system."_sv);
@ -37,12 +12,11 @@ Result<int> luna_main(int argc, char** argv)
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);
parser.add_switch_argument(verbose, 'v', "verbose"_sv, "log every removed file and directory"_sv);
parser.parse(argc, argv);
if (!recursive) TRY(remove_wrapper(path, verbose));
if (!recursive) TRY(os::FileSystem::remove(path));
else
TRY(remove_tree(path, verbose));
TRY(os::FileSystem::remove_tree(path));
return 0;
}

23
apps/signal-test.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <signal.h>
#include <stdio.h>
#include <string.h>
void handler(int)
{
puts("I caught a segfault!");
}
int main()
{
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
sigaction(SIGSEGV, &sa, NULL);
#pragma GCC diagnostic ignored "-Wnonnull"
char* str = nullptr;
memset(str, 0, 2);
return 0;
}

View File

@ -2,7 +2,7 @@
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
static const char* file_type(mode_t mode)
{
@ -24,7 +24,7 @@ Result<int> luna_main(int argc, char** argv)
bool follow_symlinks { false };
os::ArgumentParser parser;
parser.add_description("Display file metadata.");
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");
@ -36,20 +36,10 @@ Result<int> luna_main(int argc, char** argv)
char buf[11];
os::format_mode(st.st_mode, buf);
char atime[256];
strftime(atime, sizeof(atime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_atim.tv_sec));
char mtime[256];
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_mtim.tv_sec));
char ctime[256];
strftime(ctime, sizeof(ctime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_ctim.tv_sec));
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("Access: %s.%.9ld\n", atime, st.st_atim.tv_nsec);
printf("Modify: %s.%.9ld\n", mtime, st.st_mtim.tv_nsec);
printf("Change: %s.%.9ld\n", ctime, st.st_ctim.tv_nsec);
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);
return 0;
}

View File

@ -127,7 +127,7 @@ Result<int> luna_main(int argc, char** argv)
{
chdir(entry->pw_dir);
clearenv();
setenv("PATH", "/usr/bin:/usr/local/bin", 1);
setenv("PATH", "/bin:/sbin", 1);
setpgid(0, 0);
}

View File

@ -1,4 +1,4 @@
#include <moon/Syscall.h>
#include <luna/Syscall.h>
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <stdlib.h>

View File

@ -1,47 +0,0 @@
#include <errno.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <sys/stat.h>
Result<int> luna_main(int argc, char** argv)
{
Vector<StringView> files;
bool only_atime { false };
bool only_mtime { false };
bool no_create { false };
bool no_dereference { false };
os::ArgumentParser parser;
parser.add_description("Update the access and modification times of files."_sv);
parser.add_system_program_info("touch"_sv);
parser.set_vector_argument(files, "files", true);
parser.add_switch_argument(only_atime, 'a', ""_sv, "change only the access time"_sv);
parser.add_switch_argument(no_create, 'c', "no-create"_sv, "do not create new files"_sv);
parser.add_switch_argument(no_dereference, 'h', "no-dereference"_sv, "do not follow symbolic links"_sv);
parser.add_switch_argument(only_mtime, 'm', ""_sv, "change only the modification time"_sv);
TRY(parser.parse(argc, argv));
if (only_atime && only_mtime)
{
os::eprintln("%s: only one of -a and -m can be specified.", argv[0]);
parser.short_usage(argv[0]);
}
struct timespec times[2] = {
{ .tv_sec = 0, .tv_nsec = only_mtime ? UTIME_OMIT : UTIME_NOW },
{ .tv_sec = 0, .tv_nsec = only_atime ? UTIME_OMIT : UTIME_NOW },
};
for (auto& filename : files)
{
SharedPtr<os::File> file;
if (no_create) file = TRY(os::File::open(filename, os::File::ReadOnly));
else
file = TRY(os::File::open_or_create(filename, os::File::ReadOnly));
if (futimens(file->fd(), times, no_dereference ? AT_SYMLINK_NOFOLLOW : 0) < 0) return err(errno);
}
return 0;
}

View File

@ -1,4 +1,4 @@
Name=mount-home
Description=Mount the user's home directory on a writable filesystem.
Command=/etc/startup/mount-home.sh
Script=/etc/startup/mount-home.sh
Wait=true

View File

@ -1,2 +1,2 @@
root:toor:0:0:Administrator:/:/usr/bin/sh
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
root:toor:0:0:Administrator:/:/bin/sh
selene:moon:1000:1000:User:/home/selene:/bin/sh

View File

@ -1,14 +0,0 @@
Welcome to the Luna operating system!
You are running on the default user account, selene.
If you are familiar with Unix-style operating systems (like Linux or *BSD), you should be able to use the Luna terminal without much problems.
Following the traditional Unix filesystem structure, programs are installed in /usr/bin (/bin is a symlink to /usr/bin). The command `ls /bin` will show all commands available on your current Luna installation.
Currently, because of driver limitations, the root file system is mounted read-only. Your home folder is writable, but volatile; it is created and populated on boot, and its contents will vanish after a reboot.
The system is booted using the 'init' program. You can read its configuration files in the /etc/init directory to learn more about the boot process.
Luna is free software, released under the BSD-2-Clause license. The license is included in the LICENSE file in your home directory.
View the source code and read more about Luna at https://git.cloudapio.eu/apio/Luna.

View File

@ -1,6 +1,2 @@
#!/bin/sh
# Create and populate a volatile home directory.
mount -t tmpfs tmpfs /home/selene
chown selene:selene /home/selene
cp /etc/skel/welcome /home/selene/
cp /etc/skel/LICENSE /home/selene/

View File

@ -6,7 +6,6 @@ set(SOURCES
${HEADERS}
src/main.cpp
src/Log.cpp
src/Pledge.cpp
src/cxxabi.cpp
src/video/Framebuffer.cpp
src/video/TextConsole.cpp
@ -15,7 +14,6 @@ set(SOURCES
src/memory/KernelVM.cpp
src/memory/AddressSpace.cpp
src/memory/MemoryMap.cpp
src/memory/SharedMemory.cpp
src/boot/Init.cpp
src/arch/Serial.cpp
src/arch/Timer.cpp
@ -43,36 +41,24 @@ set(SOURCES
src/sys/mount.cpp
src/sys/resource.cpp
src/sys/signal.cpp
src/sys/socket.cpp
src/sys/poll.cpp
src/sys/alarm.cpp
src/sys/pledge.cpp
src/sys/memstat.cpp
src/fs/VFS.cpp
src/fs/Pipe.cpp
src/fs/Mount.cpp
src/fs/MBR.cpp
src/fs/GPT.cpp
src/fs/StorageCache.cpp
src/net/UnixSocket.cpp
src/fs/tmpfs/FileSystem.cpp
src/fs/tmpfs/Inode.cpp
src/fs/ext2/FileSystem.cpp
src/fs/ext2/Inode.cpp
src/fs/devices/DeviceRegistry.cpp
src/fs/devices/BlockDevice.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/devices/UARTDevice.cpp
src/fs/devices/MouseDevice.cpp
src/fs/devices/KeyboardDevice.cpp
src/fs/InitRD.cpp
src/binfmt/ELF.cpp
src/binfmt/BinaryFormat.cpp
src/binfmt/Script.cpp
src/thread/ELF.cpp
)
if("${LUNA_ARCH}" MATCHES "x86_64")
@ -90,7 +76,6 @@ if("${LUNA_ARCH}" MATCHES "x86_64")
src/arch/x86_64/init/GDT.cpp
src/arch/x86_64/init/IDT.cpp
src/arch/x86_64/init/PIC.cpp
src/arch/x86_64/PS2Mouse.cpp
)
endif()

View File

@ -15,7 +15,3 @@
# control characters, leading/trailing spaces, problematic characters and invalid UTF-8). Keep in mind that this restriction
# is only enforced when creating files; existing files with such illegal filenames are parsed correctly and fully usable.
# target_compile_definitions(moon PRIVATE MOON_DISABLE_FILENAME_RESTRICTIONS)
# Uncomment the line below to make the kernel also calculate stack traces for userspace addresses on program crashes.
# This can aid in debugging, but makes the kernel more unstable as stack tracing will access arbitrary userspace memory.
# target_compile_definitions(moon PRIVATE MOON_ENABLE_USERSPACE_STACK_TRACES)

View File

@ -11,5 +11,4 @@ target_compile_definitions(moon PRIVATE EXT2_DEBUG)
target_compile_definitions(moon PRIVATE DEVICE_REGISTRY_DEBUG)
target_compile_definitions(moon PRIVATE FORK_DEBUG)
target_compile_definitions(moon PRIVATE MOUNT_DEBUG)
target_compile_definitions(moon PRIVATE CACHE_DEBUG)
target_compile_options(moon PRIVATE -fsanitize=undefined)

View File

@ -27,8 +27,8 @@ static void log_serial(LogLevel level, const char* format, va_list origin)
va_list ap;
va_copy(ap, origin);
/*const SafeScopeLock lock { g_serial_lock };
if (!lock.did_succeed()) return;*/
const SafeScopeLock lock { g_serial_lock };
if (!lock.did_succeed()) return;
Serial::printf("\x1b[%sm"
"%c"
@ -61,7 +61,7 @@ static void log_text_console(LogLevel level, const char* format, va_list origin)
va_list ap;
va_copy(ap, origin);
/*const ScopeLock lock { g_console_lock };*/
const ScopeLock lock { g_console_lock };
const u32 original_foreground = TextConsole::foreground();
const u32 original_background = TextConsole::background();

View File

@ -1,69 +0,0 @@
#include "Pledge.h"
#include "Log.h"
#include "memory/MemoryManager.h"
static const char* promise_names[] = {
#define __enumerate(promise) #promise,
enumerate_promises(__enumerate)
#undef __enumerate
};
Result<void> check_pledge(Thread* thread, Promise promise)
{
// Thread has not called pledge().
if (thread->promises < 0) return {};
int mask = (1 << (int)promise);
if ((thread->promises & mask) != mask)
{
kerrorln("Pledge violation in thread %d! Has not pledged %s", thread->id, promise_names[(int)promise]);
if (thread->promises & (1 << (int)Promise::p_error)) return err(ENOSYS);
// Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the default
// (dump core). We could just kill the thread here and be done, but that discards anything on the current stack,
// which means that some destructors might not be called. Instead, leave the job to the next call of
// Thread::process_pending_signals().
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
// Unblock SIGABRT.
thread->signal_mask.set(SIGABRT - 1, false);
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the
// thread's pending signals.
thread->pending_signals.clear();
thread->send_signal(SIGABRT);
// This should never arrive to userspace, unless we're init and have ignored SIGABRT.
return err(ENOSYS);
}
return {};
}
Result<int> parse_promises(u64 pledge)
{
if (!pledge) return -1;
auto text = TRY(MemoryManager::strdup_from_user(pledge));
if (text.is_empty()) return 0;
auto promises = TRY(text.split(" "));
int result = 0;
for (const auto& promise : promises)
{
for (int i = 0; i < (int)Promise::num_promises; i++)
{
if (promise.view() == promise_names[i])
{
result |= (1 << i);
goto found;
}
}
return err(EINVAL);
found:
continue;
}
return result;
}

View File

@ -1,19 +0,0 @@
#pragma once
#include "thread/Thread.h"
#include <luna/Result.h>
#define enumerate_promises(_p) \
_p(stdio) _p(rpath) _p(wpath) _p(cpath) _p(fattr) _p(chown) _p(unix) _p(tty) _p(proc) _p(exec) _p(prot_exec) \
_p(id) _p(mount) _p(host) _p(error)
enum class Promise
{
#define __enumerate(promise) p_##promise,
enumerate_promises(__enumerate)
#undef __enumerate
num_promises,
};
Result<void> check_pledge(Thread* thread, Promise promise);
Result<int> parse_promises(u64 pledge);

View File

@ -1,131 +0,0 @@
#pragma once
#include <luna/Types.h>
namespace moon
{
enum KeyCode : u8
{
// Function keys
K_F1,
K_F2,
K_F3,
K_F4,
K_F5,
K_F6,
K_F7,
K_F8,
K_F9,
K_F10,
K_F11,
K_F12,
// System keys
K_Esc,
K_PrtScr,
K_Pause,
K_Super,
K_Menu,
// Modifier keys
K_LeftShift,
K_RightShift,
K_LeftAlt,
K_RightAlt, // or AltGr on some keyboards
K_LeftControl,
K_RightControl,
// Navigation keys
K_Tab,
K_Home,
K_End,
K_PageUp,
K_PageDown,
K_RightArrow,
K_LeftArrow,
K_UpArrow,
K_DownArrow,
// Editing keys
K_Backspace,
K_Enter,
K_Insert,
K_Delete,
K_KeypadEnter,
// Lock keys
K_ScrollLock,
K_CapsLock,
K_NumLock,
// Keypad keys
K_Keypad0,
K_Keypad1,
K_Keypad2,
K_Keypad3,
K_Keypad4,
K_Keypad5,
K_Keypad6,
K_Keypad7,
K_Keypad8,
K_Keypad9,
K_KeypadDot,
K_KeypadPlus,
K_KeypadMinus,
K_KeypadMul,
K_KeypadDiv,
// Character keys (depending on keyboard layout), examples in US QWERTY
K_CH00, // `
K_CH01, // 1
K_CH02, // 2
K_CH03, // 3
K_CH04, // 4
K_CH05, // 5
K_CH06, // 6
K_CH07, // 7
K_CH08, // 8
K_CH09, // 9
K_CH10, // 0
K_CH11, // -
K_CH12, // =
K_CH13, // Q
K_CH14, // W
K_CH15, // E
K_CH16, // R
K_CH17, // T
K_CH18, // Y
K_CH19, // U
K_CH20, // I
K_CH21, // O
K_CH22, // P
K_CH23, // [
K_CH24, // ]
K_CH25, // A
K_CH26, // S
K_CH27, // D
K_CH28, // F
K_CH29, // G
K_CH30, // H
K_CH31, // J
K_CH32, // K
K_CH33, // L
K_CH34, // ;
K_CH35, // '
K_CH36, // #
K_CH37, // Backslash
K_CH38, // Z
K_CH39, // X
K_CH40, // C
K_CH41, // V
K_CH42, // B
K_CH43, // N
K_CH44, // M
K_CH45, // ,
K_CH46, // .
K_CH47, // /
K_CH48, // Space
// Unknown key
K_Unknown,
};
struct [[gnu::packed]] KeyboardPacket
{
u8 key;
bool released;
};
static_assert(sizeof(KeyboardPacket) == 2);
}

View File

@ -1,22 +0,0 @@
#pragma once
#include <luna/Types.h>
namespace moon
{
enum MouseButton
{
Left = (1 << 0),
Middle = (1 << 1),
Right = (1 << 2),
};
struct [[gnu::packed]] MousePacket
{
i16 xdelta;
i16 ydelta;
u8 buttons;
u8 _padding[3];
};
static_assert(sizeof(MousePacket) == 8);
}

View File

@ -1,10 +1,9 @@
#pragma once
#include "api/Keyboard.h"
#include <luna/Option.h>
namespace Keyboard
{
struct TTYKeyboardState
struct KeyboardState
{
bool ignore_next { false };
bool left_shift { false };
@ -13,12 +12,5 @@ namespace Keyboard
bool capslock { false };
};
struct KeyboardState
{
bool ignore_next { false };
};
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state);
Option<moon::KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state);
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state);
}

View File

@ -29,15 +29,15 @@ namespace MMU
u64 translate_physical_address(u64 phys);
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages);
Result<u64> unmap(u64 virt);
Result<u64> get_physical(u64 virt);
Result<int> get_flags(u64 virt);
Result<void> remap(u64 virt, int flags);
void switch_page_directory(PageDirectory* dir);
PageDirectory* get_page_directory();
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages, PageDirectory* directory = nullptr);
Result<u64> unmap(u64 virt, PageDirectory* directory = nullptr);
Result<u64> get_physical(u64 virt, PageDirectory* directory = nullptr);
Result<int> get_flags(u64 virt, PageDirectory* directory = nullptr);
Result<void> remap(u64 virt, int flags);
void flush_all();
Result<PageDirectory*> create_page_directory_for_userspace();

View File

@ -1,12 +1,10 @@
#include "arch/CPU.h"
#include "Log.h"
#include "api/Mouse.h"
#include "arch/Keyboard.h"
#include "arch/Timer.h"
#include "arch/x86_64/CPU.h"
#include "arch/x86_64/IO.h"
#include "fs/devices/ConsoleDevice.h"
#include "fs/devices/MouseDevice.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
@ -29,9 +27,8 @@ extern void change_pic_masks(u8 pic1_mask, u8 pic2_mask);
extern void pic_eoi(unsigned char irq);
extern void pic_eoi(Registers* regs);
extern void setup_idt();
extern void init_mouse();
Thread* g_io_thread;
static Thread* g_io_thread;
typedef void (*interrupt_handler_t)(Registers*, void*);
@ -137,35 +134,27 @@ extern "C" void handle_x86_exception(Registers* regs)
case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception");
case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception");
case 21: FIXME_UNHANDLED_INTERRUPT("Control-protection exception");
default: fail("Caught invalid reserved exception or #DF/#MC, which shouldn't call handle_x86_exception");
default: FIXME_UNHANDLED_INTERRUPT("Reserved exception or #DF/#MC, which shouldn't call handle_x86_exception");
}
}
CircularQueue<u8, 60> scancode_queue;
CircularQueue<moon::MousePacket, 20> mouse_queue;
void io_thread()
{
while (true)
{
u8 scancode;
moon::MousePacket packet;
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
if (scancode_queue.try_pop(scancode)) { ConsoleDevice::did_press_or_release_key(scancode); }
else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); }
else
kernel_wait_for_event();
ConsoleDevice::did_press_or_release_key(scancode);
}
}
static void timer_interrupt(Registers* regs, void*)
{
Timer::tick();
if (should_invoke_scheduler())
{
Scheduler::invoke(regs);
TextConsole::tick_cursor();
}
if (should_invoke_scheduler()) Scheduler::invoke(regs);
}
static void keyboard_interrupt(Registers*, void*)
@ -272,8 +261,6 @@ namespace CPU
remap_pic();
init_mouse();
sync_interrupts();
}
@ -339,10 +326,7 @@ namespace CPU
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
{
StackFrame* current_frame = (StackFrame*)base_pointer;
while (current_frame &&
#ifndef MOON_ENABLE_USERSPACE_STACK_TRACES
(u64)current_frame >= 0xFFFF'FFFF'8000'0000 &&
#endif
while (current_frame && (u64)current_frame >= 0xFFFF'FFFF'8000'0000 &&
MemoryManager::validate_access(current_frame, sizeof(*current_frame), MemoryManager::DEFAULT_ACCESS) &&
current_frame->instruction)
{

View File

@ -19,20 +19,21 @@ constexpr u8 RIGHT_SHIFT = 0x36;
constexpr u8 CAPS_LOCK = 0x3a;
constexpr u8 LEFT_CONTROL = 0x1D;
constexpr u8 TAB = 0x0F;
constexpr u8 LEFT_ALT = 0x38;
constexpr u8 F11 = 0x57;
constexpr u8 F12 = 0x58;
static bool should_ignore_key(u8 scancode)
{
return (scancode > 0x3A && scancode < 0x47) || scancode == F11 || scancode == F12;
return (scancode > 0x3A && scancode < 0x47) || scancode == TAB || scancode == F11 || scancode == F12;
}
constexpr char key_table[] = {
'\0',
'\1', // escape
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', // tab
'\0', // tab
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
'\0', // left ctrl
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
@ -74,7 +75,7 @@ constexpr char shifted_key_table[] = {
'\0',
'\1', // escape
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
'\t', // tab
'\0', // tab
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
'\0', // left ctrl
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
@ -112,71 +113,7 @@ constexpr char shifted_key_table[] = {
'.', // keypad .
};
using namespace moon;
constexpr KeyCode keycode_table[] = {
K_Unknown, K_Esc,
K_CH01, // 1
K_CH02, // 2
K_CH03, // 3
K_CH04, // 4
K_CH05, // 5
K_CH06, // 6
K_CH07, // 7
K_CH08, // 8
K_CH09, // 9
K_CH10, // 0
K_CH11, // -
K_CH12, // =
K_Backspace, K_Tab,
K_CH13, // Q
K_CH14, // W
K_CH15, // E
K_CH16, // R
K_CH17, // T
K_CH18, // Y
K_CH19, // U
K_CH20, // I
K_CH21, // O
K_CH22, // P
K_CH23, // [
K_CH24, // ]
K_Enter, K_LeftControl,
K_CH25, // A
K_CH26, // S
K_CH27, // D
K_CH28, // F
K_CH29, // G
K_CH30, // H
K_CH31, // J
K_CH32, // K
K_CH33, // L
K_CH34, // ;
K_CH35, // '
K_CH00, // `
K_LeftShift,
K_CH36, // #
K_CH38, // Z
K_CH39, // X
K_CH40, // C
K_CH41, // V
K_CH42, // B
K_CH43, // N
K_CH44, // M
K_CH45, // ,
K_CH46, // .
K_CH47, // /
K_RightShift, K_KeypadMul, K_LeftAlt,
K_CH48, // Space
K_CapsLock, K_F1, K_F2, K_F3, K_F4, K_F5, K_F6,
K_F7, K_F8, K_F9, K_F10, K_NumLock, K_ScrollLock, K_Keypad7,
K_Keypad8, K_Keypad9, K_KeypadMinus, K_Keypad4, K_Keypad5, K_Keypad6, K_KeypadPlus,
K_Keypad1, K_Keypad2, K_Keypad3, K_Keypad0, K_KeypadDot, K_Unknown, K_Unknown,
K_CH37, // Backslash
K_F11, K_F12,
};
static bool is_shifted(const Keyboard::TTYKeyboardState& state)
static bool is_shifted(const Keyboard::KeyboardState& state)
{
if (state.capslock) return !(state.left_shift || state.right_shift);
return state.left_shift || state.right_shift;
@ -184,7 +121,7 @@ static bool is_shifted(const Keyboard::TTYKeyboardState& state)
namespace Keyboard
{
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state)
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state)
{
if (state.ignore_next)
{
@ -247,24 +184,4 @@ namespace Keyboard
if (is_shifted(state)) return shifted_key_table[scancode];
return key_table[scancode];
}
Option<KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state)
{
if (state.ignore_next)
{
state.ignore_next = false;
return {};
}
// FIXME: Support extended scancodes.
if (scancode == EXTENDED_KEY_CODE)
{
state.ignore_next = true;
return {};
}
bool released = is_key_released(scancode);
return KeyboardPacket { keycode_table[scancode], released };
}
}

View File

@ -110,11 +110,10 @@ namespace MMU
return result;
}
PageTableEntry& l4_entry(u64 virt, PageDirectory* directory = nullptr)
PageTableEntry& l4_entry(u64 virt)
{
auto index = l4_index(virt);
auto* vdir = directory ? translate_physical(directory) : get_virtual_page_directory();
return vdir->entries[index];
return get_virtual_page_directory()->entries[index];
}
PageDirectory& page_table(const PageTableEntry& entry)
@ -140,9 +139,9 @@ namespace MMU
return page_table(entry).entries[index];
}
Result<PageTableEntry*> find_entry(u64 virt, PageDirectory* directory = nullptr)
Result<PageTableEntry*> find_entry(u64 virt)
{
const auto& l4 = l4_entry(virt, directory);
const auto& l4 = l4_entry(virt);
if (!l4.present) return err(EFAULT);
auto& l3 = l3_entry(l4, virt);
if (!l3.present) return err(EFAULT);
@ -153,9 +152,9 @@ namespace MMU
return &l1_entry(l2, virt);
}
Result<PageTableEntry*> apply_cascading_flags(u64 virt, int flags, PageDirectory* directory = nullptr)
Result<PageTableEntry*> apply_cascading_flags(u64 virt, int flags)
{
auto& l4 = l4_entry(virt, directory);
auto& l4 = l4_entry(virt);
if (!l4.present) return err(EFAULT);
if (flags & Flags::ReadWrite) l4.read_write = true;
if (flags & Flags::User) l4.user = true;
@ -184,9 +183,9 @@ namespace MMU
entry.set_address(phys);
}
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages, PageDirectory* directory)
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages)
{
auto& l4 = l4_entry(virt, directory);
auto& l4 = l4_entry(virt);
if (!l4.present)
{
const u64 addr = TRY(MemoryManager::alloc_frame());
@ -253,9 +252,9 @@ namespace MMU
return {};
}
Result<u64> unmap(u64 virt, PageDirectory* directory)
Result<u64> unmap(u64 virt)
{
auto& l1 = *TRY(find_entry(virt, directory));
auto& l1 = *TRY(find_entry(virt));
if (!l1.present) return err(EFAULT);
const u64 address = l1.get_address();
l1.clear();
@ -263,16 +262,16 @@ namespace MMU
return address;
}
Result<u64> get_physical(u64 virt, PageDirectory* directory)
Result<u64> get_physical(u64 virt)
{
const auto& l1 = *TRY(find_entry(virt, directory));
const auto& l1 = *TRY(find_entry(virt));
if (!l1.present) return err(EFAULT);
return l1.get_address();
}
Result<int> get_flags(u64 virt, PageDirectory* directory)
Result<int> get_flags(u64 virt)
{
const auto& l1 = *TRY(find_entry(virt, directory));
const auto& l1 = *TRY(find_entry(virt));
if (!l1.present) return err(EFAULT);
return arch_flags_to_mmu(l1);
}
@ -441,8 +440,12 @@ namespace MMU
for (u64 l = 0; l < 512; l++)
{
PageTableEntry& old_l1 = old_pt->entries[l];
if (!old_l1.present) continue;
PageTableEntry& new_l1 = new_pt->entries[l];
new_l1.present = false; // The entry must be filled in by the caller.
new_l1.set_address(TRY(MemoryManager::alloc_frame()));
memcpy(&page_table(new_l1), &page_table(old_l1), ARCH_PAGE_SIZE);
}
}
}

View File

@ -1,118 +0,0 @@
#include "Log.h"
#include "api/Mouse.h"
#include "arch/CPU.h"
#include "arch/x86_64/IO.h"
#include "fs/devices/MouseDevice.h"
#include "thread/Thread.h"
extern Thread* g_io_thread;
static u8 g_mouse_packet[3];
static int g_index = 0;
#define PS2_MOUSE_Y_OVERFLOW 0x80
#define PS2_MOUSE_X_OVERFLOW 0x40
#define PS2_MOUSE_Y_SIGN 0x20
#define PS2_MOUSE_X_SIGN 0x10
#define PS2_MOUSE_ALWAYS_1 0x08
#define PS2_MOUSE_MIDDLE_BTN 0x04
#define PS2_MOUSE_RIGHT_BTN 0x02
#define PS2_MOUSE_LEFT_BTN 0x01
extern CircularQueue<moon::MousePacket, 20> mouse_queue;
static void process_mouse_event(u8 data)
{
if (g_index == 0)
{
// NOTE: https://wiki.osdev.org/Mouse_Input#PS2_Mouse says to discard the packet if X or Y overflow is 1, but
// https://wiki.osdev.org/PS2_Mouse uses it. Discard for now.
if (data & PS2_MOUSE_X_OVERFLOW) return;
if (data & PS2_MOUSE_Y_OVERFLOW) return;
if ((data & PS2_MOUSE_ALWAYS_1) == 0) return;
}
g_mouse_packet[g_index++] = data;
if (g_index < 3) return;
g_index = 0;
moon::MousePacket packet;
packet.xdelta = g_mouse_packet[1];
packet.ydelta = g_mouse_packet[2];
packet.buttons = 0;
u8 flags = g_mouse_packet[0];
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -(256 - packet.xdelta);
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -(256 - packet.ydelta);
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;
if (flags & PS2_MOUSE_LEFT_BTN) packet.buttons |= moon::MouseButton::Left;
MouseDevice::add_mouse_event(packet);
}
static void mouse_interrupt(Registers*, void*)
{
const u8 data = IO::inb(0x60);
process_mouse_event(data);
}
#define PS2_MOUSE_TIMEOUT 100000
static void mouse_wait()
{
int timeout = PS2_MOUSE_TIMEOUT;
while (--timeout)
{
if ((IO::inb(0x64) & 0b10) == 0) return;
CPU::pause();
}
}
static void mouse_wait_for_input()
{
int timeout = PS2_MOUSE_TIMEOUT;
while (--timeout)
{
if (IO::inb(0x64) & 0b1) return;
CPU::pause();
}
}
static void mouse_write_data(u8 byte)
{
mouse_wait();
IO::outb(0x64, 0xD4);
mouse_wait();
IO::outb(0x60, byte);
}
void init_mouse()
{
CPU::register_interrupt(12, mouse_interrupt, nullptr);
IO::outb(0x64, 0xA8); // Enable PS/2 auxiliary port
mouse_wait();
IO::outb(0x64, 0x20); // Get Compaq status byte
mouse_wait_for_input();
u8 status = IO::inb(0x60);
status |= 0x02; // Enable IRQ12
status &= ~0x20; // Disable Mouse Clock
mouse_wait();
IO::outb(0x64, 0x60); // Set Compaq status byte
mouse_wait();
IO::outb(0x60, status);
mouse_write_data(0xF6); // Reset defaults
mouse_wait();
IO::inb(0x60);
mouse_write_data(0xF4); // Send automatic packets when the mouse moves or is clicked
mouse_wait();
IO::inb(0x60);
g_index = 0;
}

View File

@ -106,7 +106,7 @@ bool Thread::deliver_signal(int signo, Registers* current_regs)
memcpy(&regs, current_regs, sizeof(regs));
kinfoln("signal: delivering signal %d for thread %d, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id,
kinfoln("signal: delivering signal %d for thread %ld, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id,
(void*)regs.rip, (void*)regs.rsp, (void*)handler.sa_handler, (void*)handler.__sa_sigreturn);
regs.rsp -= 128; // Skip the red zone
@ -124,7 +124,7 @@ bool Thread::deliver_signal(int signo, Registers* current_regs)
if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false;
signal_mask = handler.sa_mask;
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask.set(signo - 1, true);
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask |= (1 << (signo - 1));
rsp = regs.rsp;

View File

@ -6,7 +6,6 @@
#include "fs/MBR.h"
#include "memory/MemoryManager.h"
#include <luna/Alignment.h>
#include <luna/Buffer.h>
#include <luna/CType.h>
#include <luna/SafeArithmetic.h>
#include <luna/Vector.h>
@ -162,7 +161,7 @@ namespace ATA
{
if (!(read_bm(BusmasterRegister::Status) & BMS_IRQPending)) return;
if (m_current_drive < 2 && m_drives[m_current_drive].has_value()) m_drives[m_current_drive]->irq_handler();
if (m_current_drive < 2 && m_drives[m_current_drive]) m_drives[m_current_drive]->irq_handler();
m_irq_called = true;
@ -308,7 +307,14 @@ namespace ATA
kinfoln("ata: Channel %d has a drive on slot %d!", m_channel_index, drive);
m_drives[drive] = Drive { this, drive, {} };
auto rc = adopt_shared_if_nonnull(new (std::nothrow) Drive(this, drive, {}));
if (rc.has_error())
{
kinfoln("ata: Failed to create drive object: %s", rc.error_string());
return false;
}
m_drives[drive] = rc.release_value();
if (!m_drives[drive]->initialize())
{
@ -321,7 +327,7 @@ namespace ATA
for (u8 drive = 0; drive < 2; drive++)
{
if (m_drives[drive].has_value())
if (m_drives[drive])
{
if (!m_drives[drive]->post_initialize())
{
@ -329,7 +335,7 @@ namespace ATA
return false;
}
auto rc = ATADevice::create(m_drives[drive].value_ptr());
auto rc = ATADevice::create(m_drives[drive]);
if (rc.has_error())
{
@ -432,18 +438,6 @@ namespace ATA
m_uses_dma = false;
}
if (m_drive_index == 0 && !(status & BMS_MasterInit))
{
kwarnln("ata: Drive %d does not have DMA support", m_drive_index);
m_uses_dma = false;
}
if (m_drive_index == 1 && !(status & BMS_SlaveInit))
{
kwarnln("ata: Drive %d does not have DMA support", m_drive_index);
m_uses_dma = false;
}
auto frame = MemoryManager::alloc_frame();
if (frame.has_error() || frame.value() > 0xffffffff)
{
@ -729,7 +723,7 @@ namespace ATA
static u32 next_minor = 0;
Result<String> ATA::Drive::create_drive_name(ATA::Drive* drive)
Result<String> ATA::Drive::create_drive_name(SharedPtr<ATA::Drive> drive)
{
static u32 cd_index = 0;
static u32 sd_index = 0;
@ -737,28 +731,67 @@ Result<String> ATA::Drive::create_drive_name(ATA::Drive* drive)
return String::format("%s%d"_sv, drive->m_is_atapi ? "cd" : "sd", drive->m_is_atapi ? cd_index++ : sd_index++);
}
Result<SharedPtr<Device>> ATADevice::create(ATA::Drive* drive)
Result<SharedPtr<Device>> ATADevice::create(SharedPtr<ATA::Drive> drive)
{
auto device = TRY(adopt_shared_if_nonnull(new (std::nothrow) ATADevice(drive)));
auto device = TRY(adopt_shared_if_nonnull(new (std::nothrow) ATADevice()));
device->m_drive = drive;
device->m_device_path = TRY(ATA::Drive::create_drive_name(drive));
TRY(DeviceRegistry::register_special_device(DeviceRegistry::Disk, next_minor++, device, 0400));
return (SharedPtr<Device>)device;
}
ATADevice::ATADevice(ATA::Drive* drive) : BlockDevice(drive->block_size(), drive->block_count()), m_drive(drive)
Result<u64> ATADevice::read(u8* buf, usize offset, usize size) const
{
}
if (size == 0) return 0;
if (offset > m_drive->capacity()) return 0;
if (offset + size > m_drive->capacity()) size = m_drive->capacity() - offset;
usize length = size;
auto block_size = m_drive->block_size();
Result<void> ATADevice::read_block(Buffer& buf, u64 block) const
{
ScopedKMutexLock<100> lock(m_drive->channel()->lock());
if (buf.size() != m_drive->block_size())
// FIXME: Don't always allocate this if we don't need it.
auto* temp = TRY(make_array<u8>(block_size));
auto guard = make_scope_guard([temp] { delete[] temp; });
if (offset % block_size)
{
kwarnln("ata: error while reading block %lu: cache entry size mismatch (%lu), data=%p", block, buf.size(),
buf.data());
fail("Cache entry size mismatch");
// The size we need to read to round up to a block.
usize extra_size = block_size - (offset % block_size);
// Maybe we don't even want enough to get to the next block?
if (extra_size > size) extra_size = size;
TRY(m_drive->read_lba(offset / block_size, temp, 1));
memcpy(buf, temp + (offset % block_size), extra_size);
offset += extra_size;
size -= extra_size;
buf += extra_size;
}
return m_drive->read_lba(block, buf.data(), 1);
while (size >= ARCH_PAGE_SIZE)
{
TRY(m_drive->read_lba(offset / block_size, buf, ARCH_PAGE_SIZE / block_size));
offset += ARCH_PAGE_SIZE;
size -= ARCH_PAGE_SIZE;
buf += ARCH_PAGE_SIZE;
}
while (size >= block_size)
{
TRY(m_drive->read_lba(offset / block_size, buf, 1));
offset += block_size;
size -= block_size;
buf += block_size;
}
if (size)
{
TRY(m_drive->read_lba(offset / block_size, temp, 1));
memcpy(buf, temp, size);
}
return length;
}

View File

@ -1,6 +1,5 @@
#pragma once
#include "arch/PCI.h"
#include "fs/devices/BlockDevice.h"
#include "fs/devices/DeviceRegistry.h"
#include "lib/KMutex.h"
#include <luna/Atomic.h>
@ -165,7 +164,7 @@ namespace ATA
Result<void> read_lba(u64 lba, void* out, usize nblocks);
static Result<String> create_drive_name(ATA::Drive* drive);
static Result<String> create_drive_name(SharedPtr<ATA::Drive> drive);
private:
bool identify_ata();
@ -257,6 +256,7 @@ namespace ATA
Controller* m_controller;
u8 m_channel_index;
bool m_is_pci_native_mode;
u8 m_interrupt_line;
KMutex<100> m_lock {};
@ -271,10 +271,10 @@ namespace ATA
u8 m_current_drive = (u8)-1;
Option<Drive> m_drives[2];
SharedPtr<Drive> m_drives[2];
};
class Controller : public Shareable
class Controller
{
public:
static Result<void> scan();
@ -297,29 +297,48 @@ namespace ATA
}
class ATADevice : public BlockDevice
class ATADevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<SharedPtr<Device>> create(ATA::Drive* drive);
static Result<SharedPtr<Device>> create(SharedPtr<ATA::Drive> drive);
Result<void> read_block(Buffer& buf, u64 block) const override;
Result<usize> read(u8*, usize, usize) const override;
bool will_block_if_read() const override
Result<usize> write(const u8*, usize, usize) override
{
return err(ENOTSUP);
}
bool blocking() const override
{
return false;
}
bool is_block_device() const override
{
return true;
}
usize size() const override
{
return m_drive->capacity();
}
Result<usize> block_size() const override
{
return m_drive->block_size();
}
StringView device_path() const override
{
return m_device_path.view();
}
ATADevice(ATA::Drive* drive);
virtual ~ATADevice() = default;
private:
ATADevice() = default;
ATA::Drive* m_drive;
SharedPtr<ATA::Drive> m_drive;
String m_device_path;
};

View File

@ -1,42 +0,0 @@
#include "binfmt/BinaryFormat.h"
#include "binfmt/ELF.h"
#include "binfmt/Script.h"
struct BinaryFormatDescriptor
{
binfmt_loader_creator_t creator;
void* arg;
};
Vector<BinaryFormatDescriptor> g_binary_formats;
Result<void> BinaryFormat::init()
{
TRY(register_binary_format(ELFLoader::create, nullptr));
TRY(register_binary_format(ScriptLoader::create, nullptr));
return {};
}
Result<void> BinaryFormat::register_binary_format(binfmt_loader_creator_t creator, void* arg)
{
return g_binary_formats.try_append({ creator, arg });
}
Result<SharedPtr<BinaryFormatLoader>> BinaryFormat::create_loader(SharedPtr<VFS::Inode> inode, int recursion_level)
{
if (recursion_level >= 8) return err(ELOOP);
for (const auto& format : g_binary_formats)
{
auto loader = TRY(format.creator(inode, format.arg, recursion_level));
if (TRY(loader->sniff())) return loader;
}
return err(ENOEXEC);
}
BinaryFormatLoader::BinaryFormatLoader(SharedPtr<VFS::Inode> inode, int recursion_level)
: m_inode(inode), m_recursion_level(recursion_level)
{
}

View File

@ -1,114 +0,0 @@
#pragma once
#include "fs/VFS.h"
#include "memory/AddressSpace.h"
class BinaryFormatLoader : public Shareable
{
public:
/**
* @brief Determine if the given executable file matches this binary format.
*
* @return Result<bool> An error, or whether the file matches the binary format.
*/
virtual Result<bool> sniff() = 0;
/**
* @brief Load the given executable binary file into an address space.
*
* Depending on the binary format, this function may load an arbitrary interpreter instead, which will interpret the
* file on its own.
*
* @param space The address space to load the executable into.
* @return Result<u64> An error, or the entry point of the executable in memory.
*/
virtual Result<u64> load(AddressSpace* space) = 0;
/**
* @brief Return the short name associated with this format.
*
* @return StringView The format's name.
*/
virtual StringView format() const = 0;
/**
* @brief Transform an interpreted program's command line arguments.
*
* Example: A script 'foo.sh' with a shebang line '#!/bin/sh' is loaded using a BinaryFormatLoader.
*
* This function will then be called with path="/path/to/foo.sh" and args={"foo.sh", "--enable-bar"}
*
* The function should return {"/bin/sh", "/path/to/foo.sh", "--enable-bar"} (prepending the interpreter command
* line and substituting args[0] for the full path of the script).
*
* For native executable formats that do not require an interpreter (e.g. ELF), this function should just ignore
* path and return args unmodified.
*
* @param path The path (absolute or relative to the current process's working directory) of the current program
* file. This should be used instead of args[0] as arbitrary values can be passed there, leaving the interpreter
* unable to find the target program.
* @param args The original command line arguments passed to execve().
* @return Result<Vector<String>> An error, or the transformed command line arguments.
*/
virtual Result<Vector<String>> cmdline(const String& path, Vector<String> args) = 0;
virtual ~BinaryFormatLoader() = default;
/**
* @brief Construct a new BinaryFormatLoader.
*
* This should not be directly used, instead each subclass of BinaryFormatLoader should implement a static create()
* method which implements the binfmt_loader_creator_t type and register it using
* BinaryFormat::register_binary_format().
*
* Then, anyone that needs an appropriate BinaryFormatLoader for an executable file should call
* BinaryFormat::create_loader(), which will find an appropriate loader out of all default/registered loaders.
*
* @param inode The executable program file to load into memory.
* @param recursion_level In normal cases, 0. If the BinaryFormatLoader is created inside another loader (for
* example, a script loader loading the script's interpreter), the caller shall pass its recursion_level + 1 to
* BinaryFormat::create_loader(), which will forward it to this constructor. This avoids infinite recursion.
*/
BinaryFormatLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
protected:
SharedPtr<VFS::Inode> m_inode;
int m_recursion_level;
};
/**
* @brief The factory function signature for binary format loaders.
*/
typedef Result<SharedPtr<BinaryFormatLoader>> (*binfmt_loader_creator_t)(SharedPtr<VFS::Inode>, void*, int);
namespace BinaryFormat
{
/**
* @brief Register the default binary format loaders.
*
* @return Result<void> Whether the operation succeeded.
*/
Result<void> init();
/**
* @brief Register a new binary format loader type.
*
* @param creator A factory function to create said binary format loader. The function shall be passed the inode to
* load the program from, the arbitrary argument passed to this function, and a recursion index to avoid infinite
* recursion.
* @param arg An arbitrary argument that will be passed to the above factory function.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> register_binary_format(binfmt_loader_creator_t creator, void* arg);
/**
* @brief Create an appropriate loader object for an executable file.
*
* @param inode The executable file to create the loader for. If no appropriate loader could be found for this file
* type, this function shall return ENOEXEC.
* @param recursion_level In most cases, 0. If called inside another loader, the loader shall pass its
* own recursion_level variable + 1, to avoid infinite recursion. If recursion_level >= 8, this function shall
* immediately return ELOOP.
* @return Result<SharedPtr<BinaryFormatLoader>> An error, or the new loader object created for this file.
*/
Result<SharedPtr<BinaryFormatLoader>> create_loader(SharedPtr<VFS::Inode> inode, int recursion_level = 0);
}

View File

@ -1,154 +0,0 @@
#include "binfmt/ELF.h"
#include "Log.h"
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "memory/MemoryManager.h"
#include <bits/mmap-flags.h>
#include <luna/Alignment.h>
#include <luna/Alloc.h>
#include <luna/CString.h>
#include <luna/Common.h>
#include <luna/ScopeGuard.h>
static bool can_execute_segment(u32 flags)
{
return flags & 1;
}
static bool can_write_segment(u32 flags)
{
return flags & 2;
}
/*static bool can_write_and_execute_segment(u32 flags)
{
return can_write_segment(flags) && can_execute_segment(flags);
}*/
Result<bool> ELFLoader::sniff()
{
u8 buf[SELFMAG];
usize nread = TRY(m_inode->read(buf, 0, sizeof buf));
if (nread < SELFMAG) return false;
return !memcmp(buf, ELFMAG, SELFMAG);
}
Result<u64> ELFLoader::load(AddressSpace* space)
{
Elf64_Ehdr elf_header;
usize nread = TRY(m_inode->read((u8*)&elf_header, 0, sizeof elf_header));
if (nread < sizeof elf_header)
{
kerrorln("Error while loading ELF: ELF header does not fit in file");
return err(ENOEXEC);
}
if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0)
{
kerrorln("Error while loading ELF: ELF header has no valid magic");
return err(ENOEXEC);
}
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
{
kerrorln("Error while loading ELF: ELF object is not 64-bit");
return err(ENOEXEC);
}
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
kerrorln("Error while loading ELF: ELF object is not 2's complement little-endian");
return err(ENOEXEC);
}
if (elf_header.e_type != ET_EXEC)
{
kerrorln("Error while loading ELF: ELF object is not an executable");
return err(ENOEXEC);
}
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());
return err(ENOEXEC);
}
if (elf_header.e_phnum == 0)
{
kerrorln("Error while loading ELF: ELF object has no program headers");
return err(ENOEXEC);
}
#ifdef ELF_DEBUG
kdbgln("ELF: Loading ELF with entry=%#.16lx", elf_header.e_entry);
#endif
usize i;
Elf64_Phdr program_header;
for (TRY(m_inode->read((u8*)&program_header, elf_header.e_phoff, sizeof program_header)), i = 0;
i < elf_header.e_phnum;
i++, TRY(m_inode->read((u8*)&program_header, elf_header.e_phoff + (i * elf_header.e_phentsize),
sizeof program_header)))
{
if (program_header.p_type == PT_LOAD)
{
#ifdef ELF_DEBUG
kdbgln("ELF: Loading segment (offset=%zu, base=%#.16lx, filesize=%zu, memsize=%zu)",
program_header.p_offset, program_header.p_vaddr, program_header.p_filesz, program_header.p_memsz);
#endif
u64 base_vaddr = align_down<ARCH_PAGE_SIZE>(program_header.p_vaddr);
u64 vaddr_diff = program_header.p_vaddr - base_vaddr;
/*expect(!can_write_and_execute_segment(program_header.p_flags),
"Segment is both writable and executable");*/
int flags = MMU::User | MMU::NoExecute;
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
if (can_execute_segment(program_header.p_flags)) flags &= ~MMU::NoExecute;
int prot = PROT_READ;
if (can_write_segment(program_header.p_flags)) prot |= PROT_WRITE;
if (can_execute_segment(program_header.p_flags)) prot |= PROT_EXEC;
if (!TRY(space->test_and_alloc_region(base_vaddr,
ceil_div(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE), prot,
MAP_ANONYMOUS | MAP_PRIVATE, 0, true)))
return err(ENOMEM);
// Allocate physical memory for the segment
TRY(MemoryManager::alloc_at(base_vaddr, ceil_div(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE),
flags));
// Zero out unused memory (before the start of the segment)
memset((void*)base_vaddr, 0, vaddr_diff);
// Load the file section of the segment
m_inode->read((u8*)program_header.p_vaddr, program_header.p_offset, program_header.p_filesz);
// Fill out the rest of the segment with 0s
memset((void*)(program_header.p_vaddr + program_header.p_filesz), 0,
program_header.p_memsz - program_header.p_filesz);
}
else { kwarnln("ELF: Encountered non-loadable program header, skipping"); }
}
return elf_header.e_entry;
}
Result<Vector<String>> ELFLoader::cmdline(const String&, Vector<String> args)
{
return args;
}
ELFLoader::ELFLoader(SharedPtr<VFS::Inode> inode, int recursion_level) : BinaryFormatLoader(inode, recursion_level)
{
}
Result<SharedPtr<BinaryFormatLoader>> ELFLoader::create(SharedPtr<VFS::Inode> inode, void*, int recursion_level)
{
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ELFLoader>(inode, recursion_level));
}

View File

@ -1,68 +0,0 @@
#include "binfmt/Script.h"
#include "binfmt/ELF.h"
#include "thread/Scheduler.h"
#define SHEBANG "#!"
Result<bool> ScriptLoader::sniff()
{
u8 buf[2];
usize nread = TRY(m_inode->read(buf, 0, sizeof buf));
if (nread < 2) return false;
return !memcmp(buf, SHEBANG, 2);
}
Result<u64> ScriptLoader::load(AddressSpace* space)
{
u8 buf[256];
usize nread = TRY(m_inode->read(buf, 2, 255));
if (!nread) return err(ENOEXEC);
for (usize i = 0; i < nread; i++)
{
if (buf[i] == '\n') buf[i] = '\0';
else if (buf[i] == '\r' && (i + 1) < nread && buf[i + 1] == '\n')
buf[i] = buf[i + 1] = '\0';
else
continue;
break;
}
auto view = StringView { (const char*)buf };
m_interpreter_cmdline = TRY(view.split(" "));
if (!m_interpreter_cmdline.size()) return err(ENOEXEC);
auto& interpreter_path = m_interpreter_cmdline[0];
auto* current = Scheduler::current();
auto interpreter =
TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, current->current_directory, true));
if (!VFS::can_execute(interpreter, current->auth)) return err(EACCES);
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
u64 entry = TRY(loader->load(space));
m_interpreter_cmdline = TRY(loader->cmdline(interpreter_path, move(m_interpreter_cmdline)));
return entry;
}
Result<Vector<String>> ScriptLoader::cmdline(const String& path, Vector<String> args)
{
Vector<String> new_args;
TRY(new_args.try_reserve(m_interpreter_cmdline.size() + args.size() + 1));
for (auto& arg : m_interpreter_cmdline) { TRY(new_args.try_append(move(arg))); }
auto arg = TRY(path.clone());
TRY(new_args.try_append(move(arg)));
for (usize i = 1; i < args.size(); i++) { TRY(new_args.try_append(move(args[i]))); }
return new_args;
}
ScriptLoader::ScriptLoader(SharedPtr<VFS::Inode> inode, int recursion_level)
: BinaryFormatLoader(inode, recursion_level)
{
}
Result<SharedPtr<BinaryFormatLoader>> ScriptLoader::create(SharedPtr<VFS::Inode> inode, void*, int recursion_level)
{
return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ScriptLoader>(inode, recursion_level));
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "binfmt/BinaryFormat.h"
#include "fs/VFS.h"
#include "memory/AddressSpace.h"
class ScriptLoader : public BinaryFormatLoader
{
public:
Result<bool> sniff() override;
Result<u64> load(AddressSpace* space) override;
Result<Vector<String>> cmdline(const String& path, Vector<String> args) override;
StringView format() const override
{
return "script";
}
ScriptLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*, int);
private:
Vector<String> m_interpreter_cmdline;
};

View File

@ -41,8 +41,8 @@ namespace GPT
u32 partition_index = 1;
auto* table = (PartitionEntry*)TRY(calloc_impl(header.num_partitions, sizeof(PartitionEntry)));
auto guard = make_scope_guard([table] { free_impl(table); });
auto* table = TRY(make_array<PartitionEntry>(header.num_partitions));
auto guard = make_scope_guard([table] { delete[] table; });
nread = TRY(device->read((u8*)table, partition_table_start, sizeof(PartitionEntry) * header.num_partitions));
check(nread == sizeof(PartitionEntry) * header.num_partitions);
@ -66,9 +66,6 @@ namespace GPT
{
header.checksum = 0;
CRC32 crc;
crc.append((u8*)&header, 0x5c);
return crc.digest();
return CRC32::checksum((u8*)&header, 0x5c);
}
}

View File

@ -6,7 +6,7 @@
#include "memory/MemoryManager.h"
#include "thread/Thread.h"
#include <bits/modes.h>
#include <luna/Common.h>
#include <luna/Alignment.h>
TarStream g_initrd;
extern const BOOTBOOT bootboot;
@ -18,15 +18,17 @@ 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)
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode, uid_t uid, gid_t gid)
{
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {});
auto rc = VFS::create_directory(path, Credentials {});
if (rc.has_error())
{
if (rc.error() == EEXIST) return {};
return rc.release_error();
}
auto dir = rc.value();
dir->chmod(mode & (mode_t)~S_IFMT);
dir->chown(uid, gid);
return {};
}
@ -37,17 +39,19 @@ Result<void> InitRD::populate_vfs()
{
if (entry.type == TarStream::EntryType::RegularFile)
{
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}));
auto file = TRY(VFS::create_file(entry.name.chars(), 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));
TRY(vfs_create_dir_if_not_exists(entry.name.chars(), entry.mode, entry.uid, entry.gid));
}
}
// Now we don't need the original initrd anymore
MemoryManager::free_frames(bootboot.initrd_ptr, ceil_div(bootboot.initrd_size, ARCH_PAGE_SIZE));
MemoryManager::free_frames(bootboot.initrd_ptr, get_blocks_from_size(bootboot.initrd_size, ARCH_PAGE_SIZE));
return {};
}

View File

@ -41,7 +41,7 @@ namespace MBR
Result<usize> write(const u8* buf, usize offset, usize length) override;
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -37,19 +37,44 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
return err(EISDIR);
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}
const VFS::InodeMetadata& metadata() const override
usize size() const override
{
return m_mount_root_inode->metadata();
return 0;
}
Result<void> set_metadata(const VFS::InodeMetadata& metadata) override
mode_t mode() const override
{
return m_mount_root_inode->set_metadata(metadata);
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
@ -57,6 +82,11 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
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;
@ -92,14 +122,14 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
return m_mount_root_inode->remove_entry(name);
}
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override
{
return m_mount_root_inode->create_file(name, mode);
return m_mount_root_inode->create_file(name);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
{
return m_mount_root_inode->create_subdirectory(name, mode);
return m_mount_root_inode->create_subdirectory(name);
}
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override

View File

@ -14,14 +14,8 @@ Result<void> Pipe::create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& w
pipe->m_reader = reader.ptr();
writer->m_pipe = reader->m_pipe = pipe;
writer->m_metadata.inum = 0;
writer->m_metadata.uid = auth.euid;
writer->m_metadata.gid = auth.egid;
writer->m_metadata.mode = 0200;
reader->m_metadata.inum = 0;
reader->m_metadata.uid = auth.euid;
reader->m_metadata.gid = auth.egid;
reader->m_metadata.mode = 0400;
writer->chown(auth.euid, auth.egid);
reader->chown(auth.euid, auth.egid);
rpipe = reader;
wpipe = writer;
@ -44,11 +38,7 @@ Result<usize> Pipe::read(u8* buf, usize, usize length)
Result<usize> Pipe::write(const u8* buf, usize, usize length)
{
if (!m_reader)
{
Scheduler::current()->send_signal(SIGPIPE);
return err(EPIPE);
}
if (!m_reader) return length;
u8* slice = TRY(m_data_buffer.slice_at_end(length));
memcpy(slice, buf, length);
@ -56,7 +46,7 @@ Result<usize> Pipe::write(const u8* buf, usize, usize length)
return length;
}
bool Pipe::will_block_if_read() const
bool Pipe::blocking() const
{
return !m_data_buffer.size() && m_writer;
}

View File

@ -6,7 +6,7 @@ class PipeInodeBase;
class PipeReader;
class PipeWriter;
class Pipe : public Shareable
class Pipe
{
public:
static Result<void> create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe);
@ -15,7 +15,7 @@ class Pipe : public Shareable
Result<usize> write(const u8* buf, usize, usize length);
bool will_block_if_read() const;
bool blocking() const;
private:
Buffer m_data_buffer;
@ -40,15 +40,14 @@ class PipeInodeBase : public VFS::FileInode
return err(ENOTSUP);
}
bool will_block_if_read() const override
bool blocking() const override
{
return m_pipe->will_block_if_read();
return m_pipe->blocking();
}
const VFS::InodeMetadata& metadata() const override
usize size() const override
{
m_metadata.size = m_pipe->m_data_buffer.size();
return m_metadata;
return m_pipe->m_data_buffer.size();
}
void did_link() override
@ -59,6 +58,23 @@ class PipeInodeBase : public VFS::FileInode
{
}
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;
@ -70,6 +86,8 @@ class PipeInodeBase : public VFS::FileInode
protected:
SharedPtr<Pipe> m_pipe;
u32 m_uid { 0 };
u32 m_gid { 0 };
};
class PipeWriter : public PipeInodeBase
@ -86,6 +104,11 @@ class PipeWriter : public PipeInodeBase
return m_pipe->write(buf, offset, length);
}
mode_t mode() const override
{
return 0200;
}
~PipeWriter();
friend class Pipe;
@ -105,6 +128,11 @@ class PipeReader : public PipeInodeBase
check(false);
}
mode_t mode() const override
{
return 0400;
}
~PipeReader();
friend class Pipe;

View File

@ -1,49 +0,0 @@
#include "fs/StorageCache.h"
#include "Log.h"
#include <luna/Heap.h>
#include <luna/ScopeGuard.h>
static LinkedList<StorageCache> g_storage_caches;
Result<StorageCache::CacheEntry*> StorageCache::fetch_entry(u64 block)
{
{
CacheEntry* entry = m_cache_entries.try_get_ref(block);
if (entry && !entry->buffer.is_empty()) return entry;
}
CacheEntry entry {};
TRY(m_cache_entries.try_set(block, move(entry)));
#ifdef CACHE_DEBUG
kdbgln("cache: Created new cache entry for block %lu", block);
#endif
return m_cache_entries.try_get_ref(block);
}
void StorageCache::clear()
{
m_mutex.lock();
kdbgln("cache: clearing %lu entries, out of %lu buckets", m_cache_entries.size(), m_cache_entries.capacity());
m_cache_entries.clear();
kdbgln("cache: done");
m_mutex.unlock();
}
StorageCache::StorageCache()
{
g_storage_caches.append(this);
}
StorageCache::~StorageCache()
{
g_storage_caches.remove(this);
}
void StorageCache::clear_caches()
{
for (auto* cache : g_storage_caches) { cache->clear(); }
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "lib/KMutex.h"
#include <luna/Buffer.h>
#include <luna/HashMap.h>
#include <luna/LinkedList.h>
class StorageCache : public LinkedListNode<StorageCache>
{
public:
struct CacheEntry
{
Buffer buffer {};
};
void lock()
{
return m_mutex.lock();
}
void unlock()
{
return m_mutex.unlock();
}
Result<CacheEntry*> fetch_entry(u64 block);
void clear();
static void clear_caches();
StorageCache();
~StorageCache();
private:
HashMap<u64, CacheEntry> m_cache_entries;
KMutex<100> m_mutex;
};

View File

@ -69,8 +69,7 @@ namespace VFS
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
}
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
SharedPtr<Inode> working_directory)
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
@ -82,11 +81,10 @@ namespace VFS
TRY(validate_filename(child_name.view()));
return parent_inode->create_subdirectory(child_name.chars(), mode);
return parent_inode->create_subdirectory(child_name.chars());
}
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
SharedPtr<Inode> working_directory)
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
@ -98,7 +96,7 @@ namespace VFS
TRY(validate_filename(child_name.view()));
return parent_inode->create_file(child_name.chars(), mode);
return parent_inode->create_file(child_name.chars());
}
Result<void> validate_filename(StringView name)
@ -137,51 +135,45 @@ namespace VFS
{
if (auth.euid == 0) return true;
const auto& metadata = inode->metadata();
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
return metadata.mode & S_IXOTH;
return inode->mode() & S_IXOTH;
}
bool can_write(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
const auto& metadata = inode->metadata();
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
return metadata.mode & S_IWOTH;
return inode->mode() & S_IWOTH;
}
bool can_read(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
const auto& metadata = inode->metadata();
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
return metadata.mode & S_IROTH;
return inode->mode() & S_IROTH;
}
bool is_setuid(SharedPtr<Inode> inode)
{
return inode->metadata().mode & S_ISUID;
return inode->mode() & S_ISUID;
}
bool is_setgid(SharedPtr<Inode> inode)
{
return inode->metadata().mode & S_ISGID;
return inode->mode() & S_ISGID;
}
bool is_sticky(SharedPtr<Inode> inode)
{
return inode->metadata().mode & S_ISVTX;
return inode->mode() & S_ISVTX;
}
bool is_seekable(SharedPtr<Inode> inode)

View File

@ -1,7 +1,5 @@
#pragma once
#include "arch/Timer.h"
#include <bits/makedev.h>
#include <bits/timespec.h>
#include <luna/SharedPtr.h>
#include <luna/StaticString.h>
#include <luna/StringView.h>
@ -19,44 +17,23 @@ namespace VFS
BlockDevice,
Symlink,
FIFO,
Socket,
};
struct InodeMetadata
{
ino_t inum;
size_t size { 0 };
mode_t mode;
nlink_t nlinks { 1 };
uid_t uid { 0 };
gid_t gid { 0 };
dev_t devid { 0 };
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
};
class Inode;
class FileSystem : public Shareable
class FileSystem
{
public:
virtual SharedPtr<Inode> root_inode() const = 0;
virtual Result<SharedPtr<Inode>> create_file_inode(mode_t mode) = 0;
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent, mode_t mode) = 0;
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor, mode_t mode) = 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<u64> allocate_inode_number()
{
return err(ENOTSUP);
}
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
virtual Result<void> reset_mount_dir() = 0;
@ -96,7 +73,7 @@ namespace VFS
StaticString<128> name;
};
class Inode : public Shareable
class Inode
{
public:
virtual Result<u64> ioctl(int, void*)
@ -109,23 +86,14 @@ namespace VFS
return err(ENOTTY);
}
virtual void did_close()
{
}
virtual Result<u64> query_shared_memory(off_t, usize)
{
return err(EACCES);
}
// Directory-specific methods
virtual Result<SharedPtr<Inode>> find(const char* name) const = 0;
virtual Option<DirectoryEntry> get(usize index) const = 0;
virtual Result<SharedPtr<Inode>> create_file(const char* name, mode_t mode) = 0;
virtual Result<SharedPtr<Inode>> create_file(const char* name) = 0;
virtual Result<SharedPtr<Inode>> create_subdirectory(const char* name, mode_t mode) = 0;
virtual Result<SharedPtr<Inode>> create_subdirectory(const char* name) = 0;
virtual Result<void> add_entry(SharedPtr<Inode> inode, const char* name) = 0;
@ -142,7 +110,7 @@ namespace VFS
virtual Result<void> truncate(usize size) = 0;
virtual bool will_block_if_read() const = 0;
virtual bool blocking() const = 0;
// Symlink-specific methods
virtual Result<StringView> readlink()
@ -150,16 +118,29 @@ namespace VFS
return StringView {};
}
virtual const InodeMetadata& metadata() const
virtual dev_t device_id() const
{
return m_metadata;
return luna_dev_makedev(0, 0);
}
virtual Result<void> set_metadata(const InodeMetadata& metadata)
// Metadata accessors
virtual usize size() const = 0;
virtual mode_t mode() const = 0;
virtual nlink_t nlinks() const
{
m_metadata = metadata;
m_metadata.ctime = *Timer::realtime_clock();
return {};
return 1;
}
virtual u32 uid() const
{
return 0;
}
virtual u32 gid() const
{
return 0;
}
virtual bool is_mountpoint() const
@ -170,6 +151,11 @@ namespace VFS
virtual void did_link() = 0;
virtual void did_unlink() = 0;
// Metadata changers
virtual Result<void> chmod(mode_t mode) = 0;
virtual Result<void> chown(u32 uid, u32 gid) = 0;
// Generic VFS-related methods
virtual FileSystem* fs() const = 0;
@ -177,6 +163,8 @@ namespace VFS
virtual InodeType type() const = 0;
virtual usize inode_number() const = 0;
virtual void add_handle()
{
auto* f = fs();
@ -188,13 +176,9 @@ namespace VFS
auto* f = fs();
if (f) f->remove_handle();
}
protected:
mutable InodeMetadata m_metadata;
Option<u64> m_shmid {};
};
class FileInode : public Inode
class FileInode : Inode
{
public:
Result<SharedPtr<Inode>> find(const char*) const override
@ -207,12 +191,12 @@ namespace VFS
return {};
}
Result<SharedPtr<Inode>> create_file(const char*, mode_t) override
Result<SharedPtr<Inode>> create_file(const char*) override
{
return err(ENOTDIR);
}
Result<SharedPtr<Inode>> create_subdirectory(const char*, mode_t) override
Result<SharedPtr<Inode>> create_subdirectory(const char*) override
{
return err(ENOTDIR);
}
@ -237,7 +221,7 @@ namespace VFS
return 0;
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}
@ -250,7 +234,7 @@ namespace VFS
virtual ~FileInode() = default;
};
class DeviceInode : public Inode
class DeviceInode : Inode
{
public:
Result<SharedPtr<Inode>> find(const char*) const override
@ -263,12 +247,12 @@ namespace VFS
return {};
}
Result<SharedPtr<Inode>> create_file(const char*, mode_t) override
Result<SharedPtr<Inode>> create_file(const char*) override
{
return err(ENOTDIR);
}
Result<SharedPtr<Inode>> create_subdirectory(const char*, mode_t) override
Result<SharedPtr<Inode>> create_subdirectory(const char*) override
{
return err(ENOTDIR);
}
@ -305,10 +289,10 @@ namespace VFS
SharedPtr<VFS::Inode> working_directory = {},
bool follow_last_symlink = true);
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {});
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {});
Result<void> validate_filename(StringView name);

View File

@ -1,51 +0,0 @@
#include "fs/devices/BlockDevice.h"
#include "arch/MMU.h"
#include <luna/Common.h>
BlockDevice::BlockDevice(u64 block_size, u64 num_blocks) : m_cache(), m_block_size(block_size), m_num_blocks(num_blocks)
{
}
Result<usize> BlockDevice::read(u8* buf, usize offset, usize length) const
{
if (length == 0) return 0;
if (offset > size()) return 0;
if (offset + length > size()) length = size() - offset;
const usize saved = length;
auto read_data = [&](u64 off, u64 len) -> Result<void> {
const u64 block = offset / m_block_size;
m_cache.lock();
auto guard = make_scope_guard([&] { m_cache.unlock(); });
auto* entry = TRY(m_cache.fetch_entry(block));
if (entry->buffer.is_empty())
{
// TODO: What if the cache needs clearing in the middle of try_resize()? That could lead to some weird
// state...
TRY(entry->buffer.try_resize(m_block_size));
TRY(read_block(entry->buffer, block));
}
memcpy(buf, entry->buffer.data() + off, len);
offset += len;
length -= len;
buf += len;
return {};
};
if (offset % m_block_size)
{
const u64 off = offset % m_block_size;
const u64 len = min(m_block_size - off, length);
TRY(read_data(off, len));
}
while (length >= m_block_size) { TRY(read_data(0, m_block_size)); }
if (length) { TRY(read_data(0, length)); }
return saved;
}

View File

@ -1,40 +0,0 @@
#pragma once
#include "fs/StorageCache.h"
#include "fs/devices/Device.h"
class BlockDevice : public Device
{
public:
BlockDevice(u64 block_size, u64 num_blocks);
Result<usize> read(u8* buf, usize offset, usize length) const override;
virtual Result<void> read_block(Buffer& buf, u64 block) const = 0;
Result<usize> write(const u8*, usize, usize) override
{
return err(ENOTSUP);
}
usize size() const override
{
return m_block_size * m_num_blocks;
}
bool is_block_device() const override
{
return true;
}
Result<usize> block_size() const override
{
return m_block_size;
}
virtual ~BlockDevice() = default;
protected:
mutable StorageCache m_cache;
u64 m_block_size;
u64 m_num_blocks;
};

View File

@ -1,8 +1,6 @@
#include "fs/devices/ConsoleDevice.h"
#include "Log.h"
#include "Pledge.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/devices/KeyboardDevice.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
#include "video/TextConsole.h"
@ -13,7 +11,6 @@
#include <luna/Vector.h>
Vector<SharedPtr<ConsoleDevice>> ConsoleDevice::m_console_devices;
bool ConsoleDevice::s_is_in_graphical_mode { false };
Result<void> ConsoleDevice::create()
{
@ -34,9 +31,9 @@ Result<void> ConsoleDevice::handle_background_process_group(bool can_succeed, in
auto foreground_pgrp = m_foreground_process_group.value();
auto* current = Scheduler::current();
if (current->pgid == foreground_pgrp) return {};
if ((pid_t)current->pgid == foreground_pgrp) return {};
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
if ((current->signal_mask & (1 << (signo - 1))) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
{
if (can_succeed) return {};
return err(EIO);
@ -52,7 +49,13 @@ Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
{
TRY(handle_background_process_group(false, SIGTTIN));
length = m_input_buffer.dequeue_data(buf, length);
if (length > m_input_buffer.size()) length = m_input_buffer.size();
memcpy(buf, m_input_buffer.data(), length);
memmove(m_input_buffer.data(), m_input_buffer.data() + length, m_input_buffer.size() - length);
m_input_buffer.try_resize(m_input_buffer.size() - length).release_value();
if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false;
@ -63,27 +66,18 @@ Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
{
if (m_settings.c_lflag & TOSTOP) TRY(handle_background_process_group(true, SIGTTOU));
if (s_is_in_graphical_mode) return length;
TextConsole::write((const char*)buf, length);
return length;
}
bool ConsoleDevice::will_block_if_read() const
bool ConsoleDevice::blocking() const
{
return m_may_read_without_blocking ? false : m_input_buffer.size() == 0;
}
void ConsoleDevice::did_press_or_release_key(u8 scancode)
{
if (!s_is_in_graphical_mode)
for (const auto& device : m_console_devices) { device->process_key_event(scancode); }
else
{
static Keyboard::KeyboardState state = {};
auto packet = Keyboard::decode_scancode(scancode, state);
if (packet.has_value()) KeyboardDevice::add_keyboard_event(packet.release_value());
}
for (const auto& device : m_console_devices) { device->process_key_event(scancode); }
}
void ConsoleDevice::process_key_event(u8 scancode)
@ -106,7 +100,6 @@ void ConsoleDevice::process_key_event(u8 scancode)
{
TextConsole::putwchar(L'\b');
if (_iscntrl(maybe_char.value())) TextConsole::putwchar(L'\b');
if (maybe_char.value() == '\t') TextConsole::wprint(L"\b\b");
}
return;
}
@ -180,7 +173,7 @@ void ConsoleDevice::process_key_event(u8 scancode)
if (m_settings.c_lflag & ECHOCTL)
{
bool should_echo = true;
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
if (key == '\n' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
if (should_echo)
{
@ -201,9 +194,6 @@ void ConsoleDevice::process_key_event(u8 scancode)
Result<u64> ConsoleDevice::ioctl(int request, void* arg)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_tty));
switch (request)
{
case TCGETS: {
@ -245,13 +235,6 @@ Result<u64> ConsoleDevice::ioctl(int request, void* arg)
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &window)) return err(EFAULT);
return 0;
}
case TTYSETGFX: {
s_is_in_graphical_mode = (bool)arg;
if (!s_is_in_graphical_mode) TextConsole::enable_cursor();
else
TextConsole::disable_cursor();
return 0;
}
default: return err(EINVAL);
}
}

View File

@ -16,7 +16,7 @@ class ConsoleDevice : public Device
static void did_press_or_release_key(u8 scancode);
bool will_block_if_read() const override;
bool blocking() const override;
Result<u64> ioctl(int request, void* arg) override;
@ -37,10 +37,9 @@ class ConsoleDevice : public Device
mutable Buffer m_input_buffer;
Option<pid_t> m_foreground_process_group {};
Vector<u8> m_line_buffer;
mutable Keyboard::TTYKeyboardState m_kb_state;
mutable Keyboard::KeyboardState m_kb_state;
static Vector<SharedPtr<ConsoleDevice>> m_console_devices;
static bool s_is_in_graphical_mode;
void process_key_event(u8 scancode);

View File

@ -1,22 +1,14 @@
#pragma once
#include "Log.h"
#include <luna/Result.h>
#include <luna/SharedPtr.h>
#include <luna/StringView.h>
#include <sys/types.h>
class Device : public Shareable
class Device
{
public:
virtual Result<usize> read(u8* buf, usize offset, usize length) const = 0;
virtual Result<usize> write(const u8* buf, usize offset, usize length) = 0;
virtual Result<u64> query_shared_memory(off_t, usize)
{
return err(EACCES);
}
virtual Result<u64> ioctl(int, void*)
{
return err(ENOTTY);
@ -48,10 +40,7 @@ class Device : public Shareable
// Path in devfs.
virtual StringView device_path() const = 0;
virtual bool will_block_if_read() const = 0;
virtual bool blocking() const = 0;
virtual ~Device() = default;
protected:
Option<u64> m_shmid;
};

View File

@ -4,8 +4,6 @@
#include "fs/devices/ConsoleDevice.h"
#include "fs/devices/FramebufferDevice.h"
#include "fs/devices/FullDevice.h"
#include "fs/devices/KeyboardDevice.h"
#include "fs/devices/MouseDevice.h"
#include "fs/devices/NullDevice.h"
#include "fs/devices/UARTDevice.h"
#include "fs/devices/ZeroDevice.h"
@ -38,7 +36,8 @@ namespace DeviceRegistry
Result<void> create_special_device_inode(SharedPtr<VFS::FileSystem> fs, const DeviceDescriptor& descriptor)
{
auto inode = TRY(fs->create_device_inode(descriptor.major, descriptor.minor, descriptor.mode));
auto inode = TRY(fs->create_device_inode(descriptor.major, descriptor.minor));
inode->chmod(descriptor.mode);
TRY(fs->root_inode()->add_entry(inode, descriptor.name));
return {};
@ -72,8 +71,6 @@ namespace DeviceRegistry
ConsoleDevice::create();
FramebufferDevice::create();
UARTDevice::create();
MouseDevice::create();
KeyboardDevice::create();
return {};
}

View File

@ -16,7 +16,6 @@ namespace DeviceRegistry
Disk = 4,
DiskPartition = 5,
Serial = 6,
Input = 7,
};
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);

View File

@ -1,6 +1,4 @@
#include "fs/devices/FramebufferDevice.h"
#include "arch/MMU.h"
#include "memory/SharedMemory.h"
#include "video/Framebuffer.h"
#include <bits/ioctl-defs.h>
#include <luna/CString.h>
@ -25,43 +23,12 @@ Result<usize> FramebufferDevice::write(const u8* buf, usize offset, usize length
return length;
}
Result<u64> FramebufferDevice::query_shared_memory(off_t offset, usize count)
{
if (offset + (count * ARCH_PAGE_SIZE) > Framebuffer::size()) return err(EINVAL);
if (!m_shmid.has_value())
{
u64 shmid = TRY(SharedMemory::create(Framebuffer::ptr() + offset, offset, count));
m_shmid = shmid;
auto* shm = g_shared_memory_map.try_get_ref(shmid);
shm->device = SharedPtr<Device> { this };
return shmid;
}
auto* shm = g_shared_memory_map.try_get_ref(*m_shmid);
if (!shm)
{
m_shmid = {};
return query_shared_memory(offset, count);
}
if (shm->offset > offset)
{
TRY(shm->grow_backward(Framebuffer::ptr() + offset, (shm->offset - offset) / ARCH_PAGE_SIZE));
}
if (shm->frames.size() < count)
{
TRY(shm->grow_forward(Framebuffer::ptr() + offset + (shm->frames.size() * ARCH_PAGE_SIZE),
count - shm->frames.size()));
}
return *m_shmid;
}
usize FramebufferDevice::size() const
{
return Framebuffer::size();
}
bool FramebufferDevice::will_block_if_read() const
bool FramebufferDevice::blocking() const
{
return false;
}

View File

@ -11,9 +11,7 @@ class FramebufferDevice : public Device
Result<usize> write(const u8*, usize, usize) override;
Result<u64> query_shared_memory(off_t offset, usize count) override;
bool will_block_if_read() const override;
bool blocking() const override;
bool is_block_device() const override
{

View File

@ -19,7 +19,7 @@ class FullDevice : public Device
return err(ENOSPC);
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -1,34 +0,0 @@
#include "fs/devices/KeyboardDevice.h"
SharedPtr<KeyboardDevice> KeyboardDevice::s_keyboard_device = {};
Result<void> KeyboardDevice::create()
{
auto device = TRY(make_shared<KeyboardDevice>());
s_keyboard_device = device;
return DeviceRegistry::register_special_device(DeviceRegistry::Input, 1, device, 0600);
}
Result<usize> KeyboardDevice::read(u8* buf, usize, usize length) const
{
length = m_packet_buffer.dequeue_data(buf, length);
return length;
}
Result<usize> KeyboardDevice::write(const u8* buf, usize, usize length)
{
TRY(m_packet_buffer.append_data(buf, length));
return length;
}
void KeyboardDevice::add_keyboard_event(const moon::KeyboardPacket& packet)
{
if (s_keyboard_device) s_keyboard_device->write((const u8*)&packet, 0, sizeof(packet));
}
bool KeyboardDevice::will_block_if_read() const
{
return !m_packet_buffer.size();
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "api/Keyboard.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/Buffer.h>
class KeyboardDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
Result<usize> read(u8*, usize, usize) const override;
Result<usize> write(const u8*, usize, usize) override;
static void add_keyboard_event(const moon::KeyboardPacket& packet);
bool will_block_if_read() const override;
StringView device_path() const override
{
return "kbd";
}
virtual ~KeyboardDevice() = default;
private:
mutable Buffer m_packet_buffer;
static SharedPtr<KeyboardDevice> s_keyboard_device;
};

View File

@ -1,50 +0,0 @@
#include "fs/devices/MouseDevice.h"
SharedPtr<MouseDevice> MouseDevice::s_mouse_device = {};
Result<void> MouseDevice::create()
{
auto device = TRY(make_shared<MouseDevice>());
s_mouse_device = device;
return DeviceRegistry::register_special_device(DeviceRegistry::Input, 0, device, 0600);
}
Result<usize> MouseDevice::read(u8* buf, usize, usize length) const
{
usize nread = 0;
while (length >= sizeof(moon::MousePacket))
{
if (!m_packet_queue.try_pop(*(moon::MousePacket*)buf)) break;
buf += sizeof(moon::MousePacket);
length -= sizeof(moon::MousePacket);
nread += sizeof(moon::MousePacket);
}
return nread;
}
Result<usize> MouseDevice::write(const u8* buf, usize, usize length)
{
usize nwritten = 0;
while (length >= sizeof(moon::MousePacket))
{
if (!m_packet_queue.try_push(*(const moon::MousePacket*)buf)) break;
buf += sizeof(moon::MousePacket);
length -= sizeof(moon::MousePacket);
nwritten += sizeof(moon::MousePacket);
}
return nwritten;
}
void MouseDevice::add_mouse_event(const moon::MousePacket& packet)
{
if (s_mouse_device) s_mouse_device->m_packet_queue.try_push(packet);
}
bool MouseDevice::will_block_if_read() const
{
return m_packet_queue.is_empty();
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "api/Mouse.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/CircularQueue.h>
class MouseDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
Result<usize> read(u8*, usize, usize) const override;
Result<usize> write(const u8*, usize, usize) override;
static void add_mouse_event(const moon::MousePacket& packet);
bool will_block_if_read() const override;
StringView device_path() const override
{
return "mouse";
}
virtual ~MouseDevice() = default;
private:
mutable CircularQueue<moon::MousePacket, 200> m_packet_queue;
static SharedPtr<MouseDevice> s_mouse_device;
};

View File

@ -17,7 +17,7 @@ class NullDevice : public Device
return 0;
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -14,7 +14,7 @@ class UARTDevice : public Device
Result<usize> write(const u8*, usize, usize) override;
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -19,7 +19,7 @@ class ZeroDevice : public Device
return 0;
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -1,6 +1,6 @@
#include "fs/ext2/FileSystem.h"
#include "fs/ext2/Inode.h"
#include <luna/Common.h>
#include <luna/Alignment.h>
static VFS::InodeType vfs_type_from_ext2_type(mode_t mode)
{
@ -49,21 +49,11 @@ namespace Ext2
auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) Ext2::Inode({}, this)));
TRY(m_host_device->read((u8*)&inode->m_raw_inode, inode_address, INODE_SIZE));
inode->m_type = vfs_type_from_ext2_type(inode->m_raw_inode.mode);
inode->m_metadata.inum = inum;
inode->m_metadata.size = (m_uses_extended_size && (inode->m_type == VFS::InodeType::RegularFile))
? ((u64)inode->m_raw_inode.size_high << 32) | (u64)inode->m_raw_inode.size_low
: inode->m_raw_inode.size_low;
inode->m_metadata.mode = inode->m_raw_inode.mode & 07777;
inode->m_metadata.nlinks = inode->m_raw_inode.nlinks;
inode->m_metadata.uid = inode->m_raw_inode.uid;
inode->m_metadata.gid = inode->m_raw_inode.gid;
inode->m_metadata.atime = { .tv_sec = inode->m_raw_inode.atime, .tv_nsec = 0 };
inode->m_metadata.mtime = { .tv_sec = inode->m_raw_inode.mtime, .tv_nsec = 0 };
inode->m_metadata.ctime = { .tv_sec = inode->m_raw_inode.create_time, .tv_nsec = 0 };
inode->m_inum = inum;
#ifdef EXT2_DEBUG
kdbgln("ext2: Read inode %lu with mode %#x (%#x + %#o), size %lu", inum, inode->m_raw_inode.mode,
inode->m_raw_inode.mode & 0xf000, inode->metadata().mode, inode->metadata().size);
inode->m_raw_inode.mode & 0xf000, inode->mode(), inode->size());
#endif
m_inode_cache.try_set(inum, inode);
@ -117,7 +107,7 @@ namespace Ext2
fs->m_host_device = host_device;
fs->m_block_size = 1024 << fs->m_superblock.log_block_size;
fs->m_block_groups = ceil_div(fs->m_superblock.nr_blocks, fs->m_superblock.blocks_per_block_group);
fs->m_block_groups = get_blocks_from_size(fs->m_superblock.nr_blocks, fs->m_superblock.blocks_per_block_group);
#ifdef EXT2_DEBUG
kdbgln("ext2: Mounting new Ext2 file system, block size=%lu, blocks=%u, inodes=%u, block group=(%u blocks, %u "

View File

@ -127,17 +127,17 @@ namespace Ext2
return m_root_inode;
}
Result<SharedPtr<VFS::Inode>> create_file_inode(mode_t) override
Result<SharedPtr<VFS::Inode>> create_file_inode() override
{
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>, mode_t) override
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>) override
{
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32, mode_t) override
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32) override
{
return err(EROFS);
}

View File

@ -7,12 +7,19 @@ namespace Ext2
{
}
usize Inode::size() const
{
return (m_fs->m_uses_extended_size && (m_type == VFS::InodeType::RegularFile))
? ((u64)m_raw_inode.size_high << 32) | (u64)m_raw_inode.size_low
: m_raw_inode.size_low;
}
Result<usize> Inode::read(u8* buf, usize offset, usize length) const
{
if (length == 0) return 0;
if (offset > m_metadata.size) return 0;
if (offset + length > m_metadata.size) length = m_metadata.size - offset;
if (offset > size()) return 0;
if (offset + length > size()) length = size() - offset;
const usize block_size = m_fs->m_block_size;
@ -88,21 +95,22 @@ namespace Ext2
{
check(m_type == VFS::InodeType::Directory);
const usize inode_size = m_metadata.size;
const usize inode_size = size();
const usize block_size = m_fs->m_block_size;
auto buf = TRY(Buffer::create_sized(block_size));
u8* const buf = TRY(make_array<u8>(block_size));
auto guard = make_scope_guard([buf] { delete[] buf; });
m_entries.clear();
for (usize offset = 0; offset < inode_size; offset += block_size)
{
TRY(read(buf.data(), offset, block_size));
TRY(read(buf, offset, block_size));
usize dir_offset = 0;
while (dir_offset < block_size)
{
auto& entry = *(Ext2::RawDirectoryEntry*)&buf.data()[dir_offset];
auto& entry = *(Ext2::RawDirectoryEntry*)&buf[dir_offset];
if (entry.inum != 0)
{
@ -176,7 +184,7 @@ namespace Ext2
if (!m_link.is_empty()) return m_link.view();
const usize length = m_metadata.size;
const usize length = size();
if (length < 60)
{

View File

@ -21,6 +21,33 @@ namespace Ext2
return m_type;
}
usize size() const override;
mode_t mode() const override
{
return m_raw_inode.mode & 07777;
}
nlink_t nlinks() const override
{
return m_raw_inode.nlinks;
}
u32 uid() const override
{
return m_raw_inode.uid;
}
u32 gid() const override
{
return m_raw_inode.gid;
}
usize inode_number() const override
{
return m_inum;
}
VFS::FileSystem* fs() const override
{
return m_fs;
@ -34,12 +61,12 @@ namespace Ext2
{
}
Result<u64> query_shared_memory(off_t, usize) override
Result<void> chmod(mode_t) override
{
return err(ENOTSUP);
return err(EROFS);
}
Result<void> set_metadata(const VFS::InodeMetadata&) override
Result<void> chown(u32, u32) override
{
return err(EROFS);
}
@ -60,14 +87,14 @@ namespace Ext2
Option<VFS::DirectoryEntry> get(usize) const override;
Result<SharedPtr<VFS::Inode>> create_file(const char*, mode_t) override
Result<SharedPtr<VFS::Inode>> create_file(const char*) override
{
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char*, mode_t) override
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char*) override
{
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
@ -95,7 +122,7 @@ namespace Ext2
return m_entries.size();
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}

View File

@ -1,5 +1,4 @@
#include "fs/tmpfs/FileSystem.h"
#include "arch/Timer.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/tmpfs/Inode.h"
#include <luna/Alloc.h>
@ -11,23 +10,17 @@ namespace TmpFS
Result<SharedPtr<VFS::FileSystem>> FileSystem::create()
{
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}, 0755));
SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}));
root->chmod(0755);
fs->set_root(root);
return (SharedPtr<VFS::FileSystem>)fs;
}
Result<u64> FileSystem::allocate_inode_number()
{
return m_next_inode_number++;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode(mode_t mode)
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode()
{
SharedPtr<FileInode> inode = TRY(make_shared<FileInode>());
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
@ -37,11 +30,10 @@ namespace TmpFS
inode->set_fs(*this, {});
TRY(inode->set_link(link, {}));
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent, mode_t mode)
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
{
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
@ -51,13 +43,11 @@ namespace TmpFS
inode->set_self(inode, {});
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor, mode_t mode)
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
{
SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));
@ -68,9 +58,6 @@ namespace TmpFS
// 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), {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
inode->m_metadata.size = device->size();
return (SharedPtr<VFS::Inode>)inode;
}

View File

@ -13,13 +13,11 @@ namespace TmpFS
return m_root_inode;
}
Result<SharedPtr<VFS::Inode>> create_file_inode(mode_t mode) override;
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent, mode_t mode) override;
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor, mode_t mode) override;
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<u64> allocate_inode_number() override;
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
Result<void> reset_mount_dir() override;

View File

@ -1,6 +1,4 @@
#include "fs/tmpfs/Inode.h"
#include "arch/MMU.h"
#include "memory/SharedMemory.h"
namespace TmpFS
{
@ -45,8 +43,6 @@ namespace TmpFS
inode->did_link();
m_metadata.mtime = *Timer::realtime_clock();
return {};
}
@ -63,23 +59,21 @@ namespace TmpFS
inode->did_unlink();
m_metadata.mtime = *Timer::realtime_clock();
return {};
}
Result<SharedPtr<VFS::Inode>> DirInode::create_file(const char* name, mode_t mode)
Result<SharedPtr<VFS::Inode>> DirInode::create_file(const char* name)
{
auto inode = TRY(m_fs->create_file_inode(mode));
auto inode = TRY(m_fs->create_file_inode());
TRY(add_entry(inode, name));
return inode;
}
Result<SharedPtr<VFS::Inode>> DirInode::create_subdirectory(const char* name, mode_t mode)
Result<SharedPtr<VFS::Inode>> DirInode::create_subdirectory(const char* name)
{
auto inode = TRY(m_fs->create_dir_inode(m_self, mode));
auto inode = TRY(m_fs->create_dir_inode(m_self));
TRY(add_entry(inode, name));
@ -116,10 +110,6 @@ namespace TmpFS
u8* slice = TRY(m_data_buffer.slice(offset, length));
memcpy(slice, buf, length);
m_metadata.size = m_data_buffer.size();
m_metadata.mtime = *Timer::realtime_clock();
return length;
}
@ -131,42 +121,11 @@ namespace TmpFS
if (size > old_size) memset(m_data_buffer.data() + old_size, 0, size - old_size);
m_metadata.size = m_data_buffer.size();
m_metadata.mtime = *Timer::realtime_clock();
return {};
}
Result<u64> FileInode::query_shared_memory(off_t offset, usize count)
usize FileInode::size() const
{
if (offset + (count * ARCH_PAGE_SIZE) > m_data_buffer.size()) return err(EINVAL);
if (!m_shmid.has_value())
{
u64 shmid = TRY(SharedMemory::create(m_data_buffer.data() + offset, offset, count));
m_shmid = shmid;
auto* shm = g_shared_memory_map.try_get_ref(shmid);
shm->inode = SharedPtr<VFS::Inode> { (VFS::Inode*)this };
return shmid;
}
auto* shm = g_shared_memory_map.try_get_ref(*m_shmid);
if (!shm)
{
m_shmid = {};
return query_shared_memory(offset, count);
}
if (shm->offset > offset)
{
TRY(shm->grow_backward(m_data_buffer.data() + offset, (shm->offset - offset) / ARCH_PAGE_SIZE));
}
if (shm->frames.size() < count)
{
TRY(shm->grow_forward(m_data_buffer.data() + offset + (shm->frames.size() * ARCH_PAGE_SIZE),
count - shm->frames.size()));
}
return *m_shmid;
return m_data_buffer.size();
}
}

View File

@ -20,29 +20,68 @@ namespace TmpFS
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_metadata.inum = inum;
m_inode_number = inum;
}
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;
Result<usize> write(const u8*, usize, usize) override;
Result<void> truncate(usize size) override;
Result<u64> query_shared_memory(off_t offset, usize count) override;
usize size() const override;
mode_t mode() const override
{
return m_mode;
}
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 mode) override
{
m_mode = mode;
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
void did_link() override
{
m_metadata.nlinks++;
m_nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
m_nlinks--;
}
virtual ~FileInode() = default;
@ -50,8 +89,11 @@ namespace TmpFS
private:
VFS::FileSystem* m_fs;
Buffer m_data_buffer;
friend class FileSystem;
usize m_inode_number;
mode_t m_mode;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
};
class SymlinkInode : public VFS::FileInode
@ -71,7 +113,7 @@ namespace TmpFS
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_metadata.inum = inum;
m_inode_number = inum;
}
Result<void> set_link(StringView link, Badge<FileSystem>)
@ -85,6 +127,11 @@ namespace TmpFS
return m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8*, usize, usize) const override
{
return err(ENOTSUP);
@ -100,14 +147,51 @@ namespace TmpFS
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_metadata.nlinks++;
m_nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
m_nlinks--;
}
Result<StringView> readlink() override
@ -120,8 +204,10 @@ namespace TmpFS
private:
VFS::FileSystem* m_fs;
String m_link;
friend class FileSystem;
usize m_inode_number;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
};
class DeviceInode : public VFS::DeviceInode
@ -141,7 +227,7 @@ namespace TmpFS
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_metadata.inum = inum;
m_inode_number = inum;
}
void set_device(SharedPtr<Device> device, Badge<FileSystem>)
@ -151,12 +237,7 @@ namespace TmpFS
void set_device_id(dev_t id, Badge<FileSystem>)
{
m_metadata.devid = id;
}
Result<u64> query_shared_memory(off_t offset, usize count) override
{
return m_device->query_shared_memory(offset, count);
m_device_id = id;
}
VFS::FileSystem* fs() const override
@ -164,6 +245,16 @@ namespace TmpFS
return m_fs;
}
dev_t device_id() const override
{
return m_device_id;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8* buf, usize offset, usize length) const override
{
return m_device->read(buf, offset, length);
@ -190,19 +281,57 @@ namespace TmpFS
return m_device->isatty();
}
bool will_block_if_read() const override
bool blocking() const override
{
return m_device->will_block_if_read();
return m_device->blocking();
}
usize size() const override
{
return m_device->size();
}
mode_t mode() const override
{
return m_mode;
}
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 mode) override
{
m_mode = mode;
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
void did_link() override
{
m_metadata.nlinks++;
m_nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
m_nlinks--;
}
virtual ~DeviceInode() = default;
@ -210,8 +339,12 @@ namespace TmpFS
private:
VFS::FileSystem* m_fs;
SharedPtr<Device> m_device;
friend class FileSystem;
usize m_inode_number;
mode_t m_mode;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
dev_t m_device_id { 0 };
};
class DirInode : public VFS::Inode
@ -226,7 +359,7 @@ namespace TmpFS
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_metadata.inum = inum;
m_inode_number = inum;
}
void set_self(SharedPtr<VFS::Inode> self, Badge<FileSystem>)
@ -252,16 +385,54 @@ namespace TmpFS
return err(EISDIR);
}
bool will_block_if_read() const override
bool blocking() const override
{
return false;
}
usize size() const override
{
return 0;
}
mode_t mode() const override
{
return m_mode;
}
u32 uid() const override
{
return m_uid;
}
u32 gid() const override
{
return m_gid;
}
Result<void> chmod(mode_t mode) override
{
m_mode = mode;
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
VFS::InodeType type() const override
{
return VFS::InodeType::Directory;
@ -284,8 +455,8 @@ namespace TmpFS
Result<void> remove_entry(const char* name) override;
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override;
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override;
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override;
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);
@ -294,9 +465,13 @@ namespace TmpFS
private:
VFS::FileSystem* m_fs;
SharedPtr<VFS::Inode> m_self;
Vector<VFS::DirectoryEntry> m_entries;
usize m_inode_number;
mode_t m_mode;
u32 m_uid { 0 };
u32 m_gid { 0 };
friend class FileSystem;
SharedPtr<VFS::Inode> m_self;
Vector<VFS::DirectoryEntry> m_entries;
};
}

View File

@ -1,7 +1,6 @@
#include "Log.h"
#include "arch/CPU.h"
#include "arch/Timer.h"
#include "binfmt/BinaryFormat.h"
#include "boot/Init.h"
#include "config.h"
#include "fs/InitRD.h"
@ -31,56 +30,38 @@ void reap_thread()
}
}
void oom_thread()
{
while (true)
{
kernel_wait_for_event();
// OOM! Do everything we can to recover memory.
StorageCache::clear_caches();
}
}
[[noreturn]] void init()
{
{
kinfoln("Starting Moon %s %s", MOON_VERSION, MOON_RELEASE);
kinfoln("Starting Moon %s, built on %s at %s", MOON_VERSION, __DATE__, __TIME__);
// Default hostname if nobody from userspace changes it
set_host_name("moon"_sv);
// 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().chars());
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());
auto root = mark_critical(TmpFS::FileSystem::create(), "Failed to create initial ramfs");
mark_critical(VFS::mount_root(root), "Failed to mount the initial ramfs as the root filesystem");
mark_critical(InitRD::populate_vfs(), "Failed to load files from the initial ramdisk");
mark_critical(DeviceRegistry::init(), "Failed to register initial devices");
auto root = mark_critical(TmpFS::FileSystem::create(), "Failed to create initial ramfs");
mark_critical(VFS::mount_root(root), "Failed to mount the initial ramfs as the root filesystem");
mark_critical(InitRD::populate_vfs(), "Failed to load files from the initial ramdisk");
mark_critical(DeviceRegistry::init(), "Failed to register initial devices");
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
auto init =
mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}), "Can't find init in the initial ramfs!");
auto init_thread =
mark_critical(Scheduler::new_userspace_thread(init, "/bin/preinit"), "Failed to create PID 1 process for init");
auto init =
mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}), "Can't find init in the initial ramfs!");
auto init_thread = mark_critical(Scheduler::new_userspace_thread(init, "/bin/preinit"),
"Failed to create PID 1 process for init");
auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
"Failed to create the process reaper kernel thread");
Scheduler::set_reap_thread(reap);
auto oom = mark_critical(Scheduler::new_kernel_thread(oom_thread, "[oom]"),
"Failed to create the out-of-memory kernel thread");
Scheduler::set_oom_thread(oom);
auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
"Failed to create the process reaper kernel thread");
Scheduler::set_reap_thread(reap);
#ifdef ARCH_X86_64
ATA::Controller::scan();
ATA::Controller::scan();
#endif
// Disable console logging before transferring control to userspace.
setup_log(log_debug_enabled(), log_serial_enabled(), false);
// Disable console logging before transferring control to userspace.
setup_log(log_debug_enabled(), log_serial_enabled(), false);
init_thread->wake_up();
}
init_thread->wake_up();
kernel_exit();
}

View File

@ -1,9 +1,7 @@
#include "memory/AddressSpace.h"
#include "Log.h"
#include "arch/MMU.h"
#include "memory/Heap.h"
#include "memory/MemoryManager.h"
#include "memory/SharedMemory.h"
#include <bits/mmap-flags.h>
#include <luna/CString.h>
#include <luna/ScopeGuard.h>
@ -51,26 +49,15 @@ Result<OwnedPtr<AddressSpace>> AddressSpace::clone()
{
OwnedPtr<AddressSpace> ptr = TRY(make_owned<AddressSpace>());
ptr->m_directory = TRY(MMU::clone_userspace_page_directory(m_directory));
for (const auto* region : m_regions)
{
auto* new_region = TRY(make<VMRegion>());
memcpy(new_region, region, sizeof(*region));
ptr->m_regions.append(new_region);
if (new_region->used && new_region->prot != 0 && new_region->flags & MAP_SHARED)
{
TRY(MemoryManager::copy_region(new_region->start, new_region->count, m_directory, ptr->m_directory));
auto* shm = g_shared_memory_map.try_get_ref(new_region->shmid);
if (shm) shm->refs++;
}
else if (new_region->used && new_region->prot != 0)
{
TRY(MemoryManager::copy_region_data(new_region->start, new_region->count, m_directory, ptr->m_directory));
}
}
ptr->m_directory = TRY(MMU::clone_userspace_page_directory(m_directory));
return move(ptr);
}
@ -94,17 +81,8 @@ AddressSpace& AddressSpace::operator=(AddressSpace&& other)
return *this;
}
Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t offset, u64 shmid, bool persistent)
Result<u64> AddressSpace::alloc_region(usize count, bool persistent)
{
auto update_region = [=](VMRegion* region) {
region->used = true;
region->persistent = persistent;
region->prot = prot;
region->flags = flags;
region->offset = offset;
region->shmid = shmid;
};
for (auto* region = m_regions.expect_last(); region; region = m_regions.previous(region).value_or(nullptr))
{
if (!region->used)
@ -112,7 +90,8 @@ Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t o
if (region->count < count) continue;
if (region->count == count)
{
update_region(region);
region->used = true;
region->persistent = persistent;
u64 address = region->start;
try_merge_region_with_neighbors(region);
return address;
@ -121,7 +100,8 @@ Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t o
u64 boundary = region->end - (count * ARCH_PAGE_SIZE);
auto* new_region = TRY(split_region(region, boundary));
update_region(new_region);
new_region->used = true;
new_region->persistent = persistent;
try_merge_region_with_neighbors(new_region);
return boundary;
@ -131,23 +111,12 @@ Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t o
return err(ENOMEM);
}
Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int prot, int flags, off_t offset, u64 shmid,
bool persistent)
Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool persistent)
{
if (address >= VM_END) return err(EINVAL);
u64 end = address + (count * ARCH_PAGE_SIZE);
auto update_region = [=](VMRegion* region) {
if (!used) region->cleanup_shared();
region->used = used;
region->persistent = persistent;
region->prot = prot;
region->flags = flags;
region->offset = offset;
region->shmid = shmid;
};
for (auto* region : m_regions)
{
if (region->end < address) continue;
@ -162,13 +131,13 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int p
if (region->start >= address && region->end <= end)
{
update_region(region);
region->used = used;
region->persistent = persistent;
if (region->start == address && region->end == end)
{
try_merge_region_with_neighbors(region);
return true;
}
try_merge_region_with_neighbors(region);
continue;
}
@ -176,7 +145,8 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int p
{
auto* middle_region = TRY(split_region(region, address));
TRY(split_region(middle_region, end));
update_region(middle_region);
middle_region->used = used;
middle_region->persistent = persistent;
return true;
}
@ -184,7 +154,8 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int p
{
bool finished = region->end == end;
auto* split = TRY(split_region(region, address));
update_region(split);
split->used = used;
split->persistent = persistent;
try_merge_region_with_neighbors(split);
if (!finished) continue;
return true;
@ -193,7 +164,8 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int p
if (region->end > end)
{
TRY(split_region(region, end));
update_region(region);
region->used = used;
region->persistent = persistent;
try_merge_region_with_neighbors(region);
return true;
}
@ -202,23 +174,6 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int p
return true;
}
Result<void> AddressSpace::sync_regions(u64 address, usize count)
{
if (address >= VM_END) return err(EINVAL);
u64 end = address + (count * ARCH_PAGE_SIZE);
for (auto* region : m_regions)
{
if (region->end < address) continue;
if (region->start > end) return {};
region->sync_shared();
}
return {};
}
void AddressSpace::merge_contiguous_regions(VMRegion* a, VMRegion* b)
{
a->end = b->end;
@ -229,24 +184,18 @@ void AddressSpace::merge_contiguous_regions(VMRegion* a, VMRegion* b)
void AddressSpace::try_merge_region_with_neighbors(VMRegion* region)
{
auto equals = [](VMRegion* a, VMRegion* b) {
if (a->used != b->used) return false;
if (a->persistent != b->persistent) return false;
if (a->prot != b->prot) return false;
if (a->flags != b->flags) return false;
if (a->shmid != b->shmid) return false;
return true;
};
auto prev = m_regions.previous(region);
if (prev.has_value() && equals(*prev, region))
if (prev.has_value() && (*prev)->used == region->used && (*prev)->persistent == region->persistent)
{
merge_contiguous_regions(*prev, region);
region = *prev;
}
auto next = m_regions.next(region);
if (next.has_value() && equals(*next, region)) { merge_contiguous_regions(region, *next); }
if (next.has_value() && (*next)->used == region->used && (*next)->persistent == region->persistent)
{
merge_contiguous_regions(region, *next);
}
}
Result<VMRegion*> AddressSpace::split_region(VMRegion* parent, u64 boundary)
@ -258,9 +207,6 @@ Result<VMRegion*> AddressSpace::split_region(VMRegion* parent, u64 boundary)
region->count = (region->end - region->start) / ARCH_PAGE_SIZE;
region->used = parent->used;
region->persistent = parent->persistent;
region->prot = parent->prot;
region->flags = parent->flags;
region->shmid = parent->shmid;
m_regions.add_after(parent, region);
parent->end = boundary;
@ -271,43 +217,6 @@ Result<VMRegion*> AddressSpace::split_region(VMRegion* parent, u64 boundary)
AddressSpace::~AddressSpace()
{
auto* directory = MMU::get_page_directory();
MMU::switch_page_directory(this->m_directory);
m_regions.consume([this](VMRegion* region) {
region->cleanup_shared();
delete region;
});
MMU::switch_page_directory(directory);
m_regions.consume([](VMRegion* region) { delete region; });
if (m_directory) MMU::delete_userspace_page_directory(m_directory);
}
void VMRegion::cleanup_shared()
{
if (used && (flags & MAP_SHARED))
{
SharedMemory* shmem = g_shared_memory_map.try_get_ref(shmid);
if (shmem)
{
for (u64 addr = start; addr < end; addr += ARCH_PAGE_SIZE) { MMU::unmap(addr); }
if (--shmem->refs == 0)
{
shmem->free();
g_shared_memory_map.try_remove(shmid);
}
}
}
}
void VMRegion::sync_shared()
{
if (used && (flags & MAP_SHARED) && (prot & PROT_WRITE))
{
SharedMemory* shmem = g_shared_memory_map.try_get_ref(shmid);
if (shmem)
{
if (shmem->inode) shmem->inode->write((const u8*)start, offset, count * ARCH_PAGE_SIZE);
if (shmem->device) shmem->device->write((const u8*)start, offset, count * ARCH_PAGE_SIZE);
}
}
}

View File

@ -3,7 +3,6 @@
#include <luna/LinkedList.h>
#include <luna/OwnedPtr.h>
#include <luna/Result.h>
#include <sys/types.h>
class VMRegion : LinkedListNode<VMRegion>
{
@ -13,13 +12,6 @@ class VMRegion : LinkedListNode<VMRegion>
usize count;
bool used { true };
bool persistent { false };
int flags { 0 };
int prot { 0 };
u64 shmid;
off_t offset { 0 };
void cleanup_shared();
void sync_shared();
};
class AddressSpace
@ -30,21 +22,18 @@ class AddressSpace
AddressSpace& operator=(AddressSpace&& other);
Result<u64> alloc_region(usize count, int prot, int flags, off_t offset, u64 shmid = 0, bool persistent = false);
Result<u64> alloc_region(usize count, bool persistent = false);
Result<bool> test_and_alloc_region(u64 address, usize count, int prot, int flags, off_t offset, u64 shmid = 0,
bool persistent = false)
Result<bool> test_and_alloc_region(u64 address, usize count, bool persistent = false)
{
return set_region(address, count, true, prot, flags, offset, shmid, persistent);
return set_region(address, count, true, persistent);
}
Result<bool> free_region(u64 address, usize count)
{
return set_region(address, count, false, 0, 0, 0, 0, false);
return set_region(address, count, false, false);
}
Result<void> sync_regions(u64 address, usize count);
static Result<OwnedPtr<AddressSpace>> try_create();
Result<OwnedPtr<AddressSpace>> clone();
@ -55,8 +44,7 @@ class AddressSpace
}
private:
Result<bool> set_region(u64 address, usize count, bool used, int prot, int flags, off_t offset, u64 shmid,
bool persistent);
Result<bool> set_region(u64 address, usize count, bool used, bool persistent);
Result<void> create_default_region();
Result<void> create_null_region();
void try_merge_region_with_neighbors(VMRegion* region);

View File

@ -1,12 +1,9 @@
#include "memory/MemoryManager.h"
#include "Log.h"
#include "arch/MMU.h"
#include "fs/StorageCache.h"
#include "memory/KernelVM.h"
#include "memory/MemoryMap.h"
#include <luna/Alignment.h>
#include <luna/Bitmap.h>
#include <luna/Common.h>
#include <luna/ScopeGuard.h>
#include <luna/Spinlock.h>
#include <luna/SystemError.h>
@ -42,11 +39,11 @@ namespace MemoryManager
Result<void> protect_kernel_sections()
{
const usize rodata_size = (usize)(end_of_kernel_rodata - start_of_kernel_rodata);
const usize rodata_pages = ceil_div(rodata_size, ARCH_PAGE_SIZE);
const usize rodata_pages = get_blocks_from_size(rodata_size, ARCH_PAGE_SIZE);
TRY(remap((u64)start_of_kernel_rodata, rodata_pages, MMU::NoExecute));
const usize data_size = (usize)(end_of_kernel_data - start_of_kernel_data);
const usize data_pages = ceil_div(data_size, ARCH_PAGE_SIZE);
const usize data_pages = get_blocks_from_size(data_size, ARCH_PAGE_SIZE);
TRY(remap((u64)start_of_kernel_data, data_pages, MMU::NoExecute | MMU::ReadWrite));
return {};
@ -69,7 +66,7 @@ namespace MemoryManager
// We store our frame bitmap at the beginning of the largest free memory block.
char* const frame_bitmap_addr = (char*)largest_free_entry.ptr();
const usize frame_bitmap_size = ceil_div(physical_address_space_size / ARCH_PAGE_SIZE, 8UL);
const usize frame_bitmap_size = get_blocks_from_size(physical_address_space_size / ARCH_PAGE_SIZE, 8UL);
// This should never happen, unless memory is very fragmented. Usually there is always a very big block of
// usable memory and then some tiny blocks around it.
@ -98,7 +95,7 @@ namespace MemoryManager
}
// Make sure that the physical frames used by the bitmap aren't handed out to anyone else.
lock_frames(largest_free_entry.address(), ceil_div(frame_bitmap_size, ARCH_PAGE_SIZE));
lock_frames(largest_free_entry.address(), get_blocks_from_size(frame_bitmap_size, ARCH_PAGE_SIZE));
}
void init()
@ -144,12 +141,7 @@ namespace MemoryManager
usize index;
bool ok = frame_bitmap->find_and_toggle(false, start_index).try_set_value(index);
if (!ok)
{
kwarnln("OOM alert! Scheduling the OOM thread...");
Scheduler::signal_oom_thread();
return err(ENOMEM);
}
if (!ok) return err(ENOMEM);
start_index = index + 1;
@ -234,57 +226,6 @@ namespace MemoryManager
return {};
}
Result<void> copy_region(u64 virt, usize count, PageDirectory* oldpd, PageDirectory* newpd)
{
CHECK_PAGE_ALIGNED(virt);
usize pages_mapped = 0;
// Let's clean up after ourselves if we fail.
auto guard = make_scope_guard(
[=, &pages_mapped] { kwarnln("copy_region failed, sorry! cannot reclaim already copied pages"); });
while (pages_mapped < count)
{
u64 phys = TRY(MMU::get_physical(virt, oldpd));
int flags = TRY(MMU::get_flags(virt, oldpd));
TRY(MMU::map(virt, phys, flags, MMU::UseHugePages::No, newpd));
virt += ARCH_PAGE_SIZE;
pages_mapped++;
}
guard.deactivate();
return {};
}
Result<void> copy_region_data(u64 virt, usize count, PageDirectory* oldpd, PageDirectory* newpd)
{
CHECK_PAGE_ALIGNED(virt);
usize pages_mapped = 0;
// Let's clean up after ourselves if we fail.
auto guard = make_scope_guard(
[=, &pages_mapped] { kwarnln("copy_region_data failed, sorry! cannot reclaim already copied pages"); });
while (pages_mapped < count)
{
u64 frame = TRY(alloc_frame());
u64 phys = TRY(MMU::get_physical(virt, oldpd));
int flags = TRY(MMU::get_flags(virt, oldpd));
memcpy((void*)MMU::translate_physical_address(frame), (void*)MMU::translate_physical_address(phys),
ARCH_PAGE_SIZE);
TRY(MMU::map(virt, frame, flags, MMU::UseHugePages::No, newpd));
virt += ARCH_PAGE_SIZE;
pages_mapped++;
}
guard.deactivate();
return {};
}
Result<void> map_huge_frames_at(u64 virt, u64 phys, usize count, int flags)
{
CHECK_PAGE_ALIGNED(virt);
@ -417,20 +358,6 @@ namespace MemoryManager
return {};
}
Result<void> unmap_owned_if_possible(u64 virt, usize count)
{
CHECK_PAGE_ALIGNED(virt);
while (count--)
{
const auto frame = MMU::unmap(virt);
if (frame.has_value()) TRY(free_frame(frame.value()));
virt += ARCH_PAGE_SIZE;
}
return {};
}
Result<void> unmap_owned_and_free_vm(u64 virt, usize count)
{
CHECK_PAGE_ALIGNED(virt);
@ -515,7 +442,7 @@ namespace MemoryManager
{
TRY(result.try_append(*(char*)address));
address++;
if ((address % ARCH_PAGE_SIZE) == 0)
if (address % ARCH_PAGE_SIZE)
{
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
}
@ -533,7 +460,7 @@ namespace MemoryManager
uintptr_t diff = address - page;
usize pages = ceil_div(size + diff, ARCH_PAGE_SIZE);
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
while (pages--)
{
@ -569,7 +496,7 @@ namespace MemoryManager
while (size--)
{
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
if ((user_ptr % ARCH_PAGE_SIZE) == 0)
if (user_ptr % ARCH_PAGE_SIZE)
{
if (!validate_page_access(user_ptr, MMU::ReadWrite | MMU::User)) return false;
}
@ -597,7 +524,7 @@ namespace MemoryManager
while (size--)
{
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
if ((user_ptr % ARCH_PAGE_SIZE) == 0)
if (user_ptr % ARCH_PAGE_SIZE)
{
if (!validate_page_access(user_ptr, MMU::User)) return false;
}

View File

@ -67,9 +67,6 @@ namespace MemoryManager
Result<void> map_frames_at(u64 virt, u64 phys, usize count, int flags);
Result<void> map_huge_frames_at(u64 virt, u64 phys, usize count, int flags);
Result<void> copy_region(u64 virt, usize count, PageDirectory* oldpd, PageDirectory* newpd);
Result<void> copy_region_data(u64 virt, usize count, PageDirectory* oldpd, PageDirectory* newpd);
Result<u64> alloc_at(u64 virt, usize count, int flags);
Result<u64> alloc_at_zeroed(u64, usize count, int flags);
Result<u64> alloc_for_kernel(usize count, int flags);
@ -78,7 +75,6 @@ namespace MemoryManager
Result<void> unmap_owned(u64 virt, usize count);
Result<void> unmap_owned_and_free_vm(u64 virt, usize count);
Result<void> unmap_owned_if_possible(u64 virt, usize count);
Result<void> unmap_weak(u64 virt, usize count);
Result<void> unmap_weak_and_free_vm(u64 virt, usize count);

View File

@ -1,124 +0,0 @@
#include "memory/SharedMemory.h"
#include "memory/MemoryManager.h"
#include <bits/mmap-flags.h>
#include <luna/ScopeGuard.h>
HashMap<u64, SharedMemory> g_shared_memory_map;
Atomic<u64> g_next_shmem_id;
Result<u64> SharedMemory::create(u8* mem, off_t offset, usize count)
{
SharedMemory shmem;
shmem.offset = offset;
auto guard = make_scope_guard([&shmem] {
for (u64 frame : shmem.frames) { MemoryManager::free_frame(frame); }
});
while (count--)
{
u64 frame = TRY(MemoryManager::alloc_frame());
TRY(shmem.frames.try_append(frame));
if (mem)
{
memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE);
mem += ARCH_PAGE_SIZE;
}
else { memset((void*)MMU::translate_physical_address(frame), 0, ARCH_PAGE_SIZE); }
}
const u64 id = g_next_shmem_id++;
check(TRY(g_shared_memory_map.try_set(id, move(shmem))));
guard.deactivate();
return id;
}
Result<void> SharedMemory::grow_forward(u8* mem, usize count)
{
u64 old_count = frames.size();
auto guard = make_scope_guard([old_count, this] {
while (old_count < this->frames.size()) { MemoryManager::free_frame(*this->frames.try_pop()); }
});
while (count--)
{
u64 frame = TRY(MemoryManager::alloc_frame());
TRY(frames.try_append(frame));
if (mem)
{
memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE);
mem += ARCH_PAGE_SIZE;
}
}
guard.deactivate();
return {};
}
Result<void> SharedMemory::grow_backward(u8* mem, usize count)
{
Vector<u64> new_frames;
const usize bytes = count * ARCH_PAGE_SIZE;
auto guard = make_scope_guard([&new_frames, count] {
for (u64 i = 0; i < count && i < new_frames.size(); i++) { MemoryManager::free_frame(new_frames[i]); }
});
while (count--)
{
u64 frame = TRY(MemoryManager::alloc_frame());
TRY(new_frames.try_append(frame));
if (mem)
{
memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE);
mem += ARCH_PAGE_SIZE;
}
}
for (u64 frame : frames) { TRY(new_frames.try_append(frame)); }
frames = move(new_frames);
offset -= bytes;
guard.deactivate();
return {};
}
Result<void> SharedMemory::map(u64 virt, int flags, off_t _offset, usize count)
{
usize off = _offset / ARCH_PAGE_SIZE;
if (off + count > frames.size()) return err(EINVAL);
for (usize i = off; i < count; i++)
{
TRY(MMU::map(virt, frames[i], flags, MMU::UseHugePages::No));
virt += ARCH_PAGE_SIZE;
}
return {};
}
void SharedMemory::free()
{
if ((inode || device) && (prot & PROT_WRITE))
{
for (u64 i = 0; i < frames.size(); i++)
{
if (inode)
inode->write((u8*)MMU::translate_physical_address(frames[i]), offset + (i * ARCH_PAGE_SIZE),
ARCH_PAGE_SIZE);
if (device)
device->write((u8*)MMU::translate_physical_address(frames[i]), offset + (i * ARCH_PAGE_SIZE),
ARCH_PAGE_SIZE);
}
}
for (u64 frame : frames) { MemoryManager::free_frame(frame); }
}

View File

@ -1,32 +0,0 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/Device.h"
#include <luna/HashMap.h>
#include <luna/Vector.h>
#include <sys/types.h>
struct SharedMemory
{
Vector<u64> frames;
off_t offset;
int prot;
SharedPtr<VFS::Inode> inode {};
SharedPtr<Device> device {};
int refs { 0 };
SharedMemory() = default;
SharedMemory(SharedMemory&&) = default;
static Result<u64> create(u8* mem, off_t offset, usize count);
Result<void> grow_forward(u8* mem, usize count);
Result<void> grow_backward(u8* mem, usize count);
Result<void> map(u64 virt, int flags, off_t offset, usize count);
void free();
~SharedMemory() = default;
};
extern HashMap<u64, SharedMemory> g_shared_memory_map;

View File

@ -1,72 +0,0 @@
#pragma once
#include "arch/CPU.h"
#include "fs/VFS.h"
#include "thread/Thread.h"
#include <bits/socket.h>
class Socket : public VFS::FileInode
{
public:
Socket() = default;
VFS::InodeType type() const override
{
return VFS::InodeType::Socket;
}
void set_fs(VFS::FileSystem* fs)
{
m_fs = fs;
}
void set_inode_number(usize inum)
{
m_metadata.inum = inum;
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
Result<usize> read(u8* buf, usize, usize length) const override
{
return recv(buf, length, 0);
}
Result<usize> write(const u8* buf, usize, usize length) override
{
return send(buf, length, 0);
}
virtual Result<usize> send(const u8*, usize, int) = 0;
virtual Result<usize> recv(u8*, usize, int) const = 0;
virtual Result<void> bind(struct sockaddr*, socklen_t) = 0;
virtual Result<void> connect(Registers*, int, struct sockaddr*, socklen_t) = 0;
virtual Result<SharedPtr<OpenFileDescription>> accept(Registers*, int, struct sockaddr**, socklen_t*) = 0;
virtual Result<void> listen(int backlog) = 0;
Result<void> truncate(usize) override
{
return err(EINVAL);
}
void did_link() override
{
m_metadata.nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
}
virtual ~Socket() = default;
protected:
VFS::FileSystem* m_fs { nullptr };
};

View File

@ -1,211 +0,0 @@
#include "net/UnixSocket.h"
#include <bits/open-flags.h>
#include <luna/PathParser.h>
#include <thread/Scheduler.h>
UnixSocket::UnixSocket()
{
}
UnixSocket::UnixSocket(UnixSocket* peer) : m_state(State::Connected), m_peer(peer)
{
}
UnixSocket::~UnixSocket()
{
did_close();
}
void UnixSocket::did_close()
{
if (m_peer)
{
m_peer->m_peer = nullptr;
m_peer->m_state = State::Reset;
}
m_state = State::Inactive;
}
void UnixSocket::connect_to_peer(UnixSocket* peer)
{
m_peer = peer;
m_state = State::Connected;
}
Result<usize> UnixSocket::send(const u8* buf, usize length, int)
{
if (m_state == State::Reset) return err(ECONNRESET);
if (m_state != State::Connected) return err(ENOTCONN);
check(m_peer);
TRY(m_peer->m_data.append_data(buf, length));
return length;
}
Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
{
if (m_state == State::Reset && !m_data.size()) return err(ECONNRESET);
if (m_state != State::Connected && m_state != State::Reset) return err(ENOTCONN);
return m_data.dequeue_data(buf, length);
}
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
SharedPtr<UnixSocket> socket)
{
auto parent_path = TRY(PathParser::dirname(path));
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, working_directory));
if (!VFS::can_write(parent_inode, auth)) return err(EACCES);
auto child_name = TRY(PathParser::basename(path));
TRY(VFS::validate_filename(child_name.view()));
socket->set_inode_number(TRY(parent_inode->fs()->allocate_inode_number()));
socket->set_fs(parent_inode->fs());
return parent_inode->add_entry(socket, child_name.chars());
}
Result<void> UnixSocket::bind(struct sockaddr* addr, socklen_t addrlen)
{
if (!addr) return err(EDESTADDRREQ);
if (addr->sa_family != AF_UNIX) return err(EAFNOSUPPORT);
if ((usize)addrlen > sizeof(sockaddr_un)) return err(EINVAL);
if (m_state == State::Connected) return err(EISCONN);
if (m_state != State::Inactive) return err(EINVAL);
struct sockaddr_un* un_address = (struct sockaddr_un*)addr;
String path = TRY(String::from_string_view(
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
auto* current = Scheduler::current();
m_metadata.mode = 0777 & ~current->umask;
m_metadata.uid = current->auth.euid;
m_metadata.gid = current->auth.egid;
auto rc = bind_socket_to_fs(path.chars(), current->auth, current->current_directory, SharedPtr<Socket> { this });
if (rc.has_error())
{
if (rc.error() == EEXIST) return err(EADDRINUSE);
return rc.release_error();
}
memcpy(&m_addr, un_address, addrlen);
m_addrlen = addrlen;
m_state = State::Bound;
return {};
}
Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* addr, socklen_t addrlen)
{
if (!addr) return err(EINVAL);
if (addr->sa_family != AF_UNIX) return err(EAFNOSUPPORT);
if ((usize)addrlen > sizeof(sockaddr_un)) return err(EINVAL);
if (m_state == State::Connected) return err(EISCONN);
if (m_state == State::Connecting) return err(EALREADY);
if (m_state != State::Inactive) return err(EINVAL);
struct sockaddr_un* un_address = (struct sockaddr_un*)addr;
String path = TRY(String::from_string_view(
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
auto* current = Scheduler::current();
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
if (inode->type() != VFS::InodeType::Socket)
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
auto socket = (SharedPtr<UnixSocket>)inode;
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
if (!socket->m_listen_queue.try_push(this)) return err(ECONNREFUSED);
if (socket->m_blocked_thread) socket->m_blocked_thread->wake_up();
m_state = Connecting;
if (flags & O_NONBLOCK) return err(EINPROGRESS);
while (1)
{
m_blocked_thread = current;
kernel_wait_for_event();
m_blocked_thread = nullptr;
if (current->interrupted)
{
if (current->will_ignore_pending_signal())
{
current->process_pending_signals(regs);
continue;
}
return err(EINTR);
}
break;
}
check(m_state == Connected);
check(m_peer);
return {};
}
Result<void> UnixSocket::listen(int backlog)
{
if (backlog < 0) backlog = 0;
if (m_state == State::Listening || m_state == State::Connected) return err(EINVAL);
if (m_state != State::Bound) return err(EDESTADDRREQ);
TRY(m_listen_queue.set_size(backlog));
m_state = State::Listening;
return {};
}
Result<SharedPtr<OpenFileDescription>> UnixSocket::accept(Registers* regs, int flags, struct sockaddr** addr,
socklen_t* addrlen)
{
if (m_state != State::Listening) return err(EINVAL);
auto* current = Scheduler::current();
UnixSocket* peer = nullptr;
while (!m_listen_queue.try_pop(peer))
{
if (flags & O_NONBLOCK) return err(EAGAIN);
m_blocked_thread = current;
kernel_wait_for_event();
m_blocked_thread = nullptr;
if (current->interrupted)
{
if (current->will_ignore_pending_signal())
{
current->process_pending_signals(regs);
continue;
}
return err(EINTR);
}
}
check(peer);
auto socket = TRY(make_shared<UnixSocket>(peer));
auto description = TRY(make_shared<OpenFileDescription>(socket, O_RDWR));
peer->m_peer = socket.ptr();
peer->m_state = State::Connected;
if (peer->m_blocked_thread) peer->m_blocked_thread->wake_up();
*addr = (struct sockaddr*)&peer->m_addr;
*addrlen = peer->m_addrlen;
return description;
}

View File

@ -1,57 +0,0 @@
#pragma once
#include "net/Socket.h"
#include "thread/Thread.h"
#include <luna/Buffer.h>
#include <luna/CircularQueue.h>
#include <luna/String.h>
#include <sys/un.h>
class UnixSocket : public Socket
{
public:
UnixSocket();
UnixSocket(UnixSocket* peer);
bool will_block_if_read() const override
{
return (m_state == Connected || m_state == Reset) && !m_data.size();
}
Result<usize> send(const u8*, usize, int) override;
Result<usize> recv(u8*, usize, int) const override;
Result<void> bind(struct sockaddr*, socklen_t) override;
Result<void> connect(Registers*, int, struct sockaddr*, socklen_t) override;
Result<SharedPtr<OpenFileDescription>> accept(Registers*, int, struct sockaddr**, socklen_t*) override;
Result<void> listen(int backlog) override;
void did_close() override;
void connect_to_peer(UnixSocket* peer);
virtual ~UnixSocket();
private:
enum State
{
Inactive,
Bound,
Listening,
Connecting,
Connected,
Reset,
};
State m_state = State::Inactive;
UnixSocket* m_peer = nullptr;
mutable Buffer m_data;
Thread* m_blocked_thread { nullptr };
DynamicCircularQueue<UnixSocket*> m_listen_queue;
struct sockaddr_un m_addr = { .sun_family = AF_UNIX, .sun_path = {} };
socklen_t m_addrlen = sizeof(sa_family_t);
};

View File

@ -1,7 +1,7 @@
#pragma once
#include "api/Syscall.h"
#include "arch/CPU.h"
#include <luna/Result.h>
#include <luna/Syscall.h>
typedef u64 SyscallArgs[6];

View File

@ -1,18 +0,0 @@
#include "Pledge.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
Result<u64> sys_alarm(Registers*, SyscallArgs args)
{
unsigned int seconds = (unsigned int)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
u64 time_left = current->alarm_ticks_left;
current->alarm_ticks_left = seconds * 1000;
return time_left * 1000;
}

View File

@ -1,4 +1,3 @@
#include "Pledge.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
@ -10,8 +9,6 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_rpath));
if (PathParser::is_absolute(path.view()))
{
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));

View File

@ -1,8 +1,6 @@
#include "Pledge.h"
#include "arch/Timer.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/clockid.h>
#include <bits/timespec.h>
@ -11,10 +9,6 @@ Result<u64> sys_clock_gettime(Registers*, SyscallArgs args)
clockid_t id = (clockid_t)args[0];
struct timespec* ts = (struct timespec*)args[1];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
switch (id)
{
case CLOCK_MONOTONIC: {

View File

@ -1,9 +1,8 @@
#include "Log.h"
#include "Pledge.h"
#include "binfmt/BinaryFormat.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>
@ -56,18 +55,13 @@ 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]));
Vector<String> envp;
if (args[2]) envp = TRY(copy_string_vector_from_userspace(args[2]));
String cmdline = TRY(String::join(argv, " "));
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();
TRY(check_pledge(current, Promise::p_exec));
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
@ -76,22 +70,9 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
kdbgln("exec: attempting to replace current image with %s", path.chars());
#endif
bool is_setuid = VFS::is_setuid(inode);
bool is_setgid = VFS::is_setgid(inode);
bool is_secure_environment = is_setgid || is_setuid;
if (is_secure_environment && current->execpromises >= 0) return err(EACCES);
auto loader = TRY(BinaryFormat::create_loader(inode));
#ifdef EXEC_DEBUG
kdbgln("exec: created loader for binary format %s", loader->format().chars());
#endif
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
auto image = TRY(ThreadImage::try_load_from_binary(loader));
argv = TRY(loader->cmdline(path, move(argv)));
auto image = TRY(ThreadImage::try_load_from_elf(inode));
u64 user_argv = TRY(image->push_string_vector_on_stack(argv));
usize user_argc = argv.size();
@ -111,13 +92,17 @@ 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 = {}; }
if (descriptor->flags & O_CLOEXEC)
{
descriptor->inode->remove_handle();
descriptor = {};
}
}
if (is_setuid) current->auth.euid = current->auth.suid = inode->metadata().uid;
if (is_setgid) current->auth.egid = current->auth.sgid = inode->metadata().gid;
if (VFS::is_setuid(inode)) current->auth.euid = current->auth.suid = inode->uid();
if (VFS::is_setgid(inode)) current->auth.egid = current->auth.sgid = inode->gid();
current->cmdline = cmdline.chars();
current->name = path.chars();
image->apply(current);
@ -125,9 +110,6 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
current->set_arguments(user_argc, user_argv, user_envc, user_envp);
current->promises = current->execpromises;
current->execpromises = -1;
memcpy(regs, &current->regs, sizeof(*regs));
for (int i = 0; i < NSIG; i++)
@ -137,7 +119,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
current->has_called_exec = true;
kinfoln("exec: thread %d was replaced with %s", current->id, path.chars());
kinfoln("exec: thread %lu was replaced with %s", current->id, path.chars());
return 0;
}
@ -146,8 +128,6 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
{
auto current = Scheduler::current();
TRY(check_pledge(current, Promise::p_proc));
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
memcpy(&current->regs, regs, sizeof(*regs));
@ -161,22 +141,28 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
thread->state = ThreadState::Runnable;
thread->is_kernel = false;
thread->fp_data.save();
thread->cmdline = current->cmdline;
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;
thread->promises = current->promises;
thread->execpromises = current->execpromises;
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
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();
}
image->apply(thread);
memcpy(&thread->regs, regs, sizeof(*regs));
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current->signal_handlers[i];
for (int i = 0; i < NSIG; i++)
{
auto sighandler = current->signal_handlers[i].sa_handler;
thread->signal_handlers[i] = { .sa_handler = sighandler, .sa_mask = 0, .sa_flags = 0 };
}
thread->signal_mask = current->signal_mask;
thread->set_return(0);
@ -184,7 +170,7 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
Scheduler::add_thread(thread);
#ifdef FORK_DEBUG
kdbgln("fork: thread %d forked into child %d", current->id, thread->id);
kdbgln("fork: thread %lu forked into child %lu", current->id, thread->id);
#endif
return thread->id;

View File

@ -1,15 +1,12 @@
#include "Log.h"
#include "Pledge.h"
#include "fs/Pipe.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/atfile.h>
#include <bits/fcntl.h>
#include <bits/open-flags.h>
#include <bits/seek.h>
#include <bits/utime.h>
#include <luna/SafeArithmetic.h>
#include <sys/types.h>
@ -25,13 +22,11 @@ Result<u64> sys_read(Registers* regs, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
if (!descriptor.is_readable()) return err(EBADF);
while (descriptor.inode()->will_block_if_read())
while (descriptor.inode->blocking())
{
if (descriptor.should_block()) kernel_sleep(10);
else
@ -40,18 +35,14 @@ Result<u64> sys_read(Registers* regs, SyscallArgs args)
if (current->interrupted)
{
kdbgln("signal: read interrupted by signal");
if (current->will_ignore_pending_signal())
{
current->process_pending_signals(regs);
continue;
}
return err(EINTR);
if (current->will_invoke_signal_handler()) return err(EINTR);
current->process_pending_signals(regs);
}
}
usize nread = TRY(descriptor.inode()->read(buf, descriptor.offset, size));
usize nread = TRY(descriptor.inode->read(buf, descriptor.offset, size));
if (VFS::is_seekable(descriptor.inode())) descriptor.offset += nread;
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nread;
return nread;
}
@ -68,18 +59,15 @@ Result<u64> sys_write(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
if (!descriptor.is_writable()) return err(EBADF);
if (descriptor.should_append() && VFS::is_seekable(descriptor.inode()))
descriptor.offset = descriptor.inode()->metadata().size;
if (descriptor.should_append() && VFS::is_seekable(descriptor.inode)) descriptor.offset = descriptor.inode->size();
usize nwritten = TRY(descriptor.inode()->write(buf, descriptor.offset, size));
usize nwritten = TRY(descriptor.inode->write(buf, descriptor.offset, size));
if (VFS::is_seekable(descriptor.inode())) descriptor.offset += nwritten;
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nwritten;
return nwritten;
}
@ -92,13 +80,11 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
if (descriptor.inode()->type() == VFS::InodeType::FIFO) return err(ESPIPE);
if (descriptor.inode->type() == VFS::InodeType::FIFO) return err(ESPIPE);
if (!VFS::is_seekable(descriptor.inode())) return descriptor.offset;
if (!VFS::is_seekable(descriptor.inode)) return descriptor.offset;
off_t new_offset;
@ -106,7 +92,7 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
{
case SEEK_SET: new_offset = offset; break;
case SEEK_CUR: new_offset = TRY(safe_add((long)descriptor.offset, offset)); break;
case SEEK_END: new_offset = TRY(safe_add((long)descriptor.inode()->metadata().size, offset)); break;
case SEEK_END: new_offset = TRY(safe_add((long)descriptor.inode->size(), offset)); break;
default: return err(EINVAL);
}
@ -124,8 +110,6 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
bool is_cloexec = true;
@ -153,14 +137,14 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
descriptor.flags &= ~O_CLOEXEC;
return 0;
}
case F_GETFL: return (u64)descriptor.status_flags();
case F_GETFL: return (u64)(descriptor.flags & ~O_CLOEXEC);
case F_SETFL: {
int arg = (int)args[2];
descriptor.status_flags() &= ~(O_APPEND | O_NONBLOCK);
descriptor.flags &= ~(O_APPEND | O_NONBLOCK);
arg &= (O_APPEND | O_NONBLOCK);
descriptor.status_flags() |= arg;
descriptor.flags |= arg;
return 0;
}
@ -177,7 +161,7 @@ Result<u64> sys_ioctl(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
auto& descriptor = *TRY(current->resolve_fd(fd));
return descriptor.inode()->ioctl(request, arg);
return descriptor.inode->ioctl(request, arg);
}
Result<u64> sys_isatty(Registers*, SyscallArgs args)
@ -185,10 +169,9 @@ Result<u64> sys_isatty(Registers*, SyscallArgs args)
int fd = (int)args[0];
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
return descriptor.inode()->isatty();
return descriptor.inode->isatty();
}
Result<u64> sys_dup2(Registers*, SyscallArgs args)
@ -198,8 +181,6 @@ Result<u64> sys_dup2(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
if (newfd < 0 || newfd >= FD_MAX) return err(EBADF);
auto descriptor = *TRY(current->resolve_fd(oldfd));
@ -218,8 +199,6 @@ Result<u64> sys_pipe(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
int rfd = TRY(current->allocate_fd(0));
int wfd = TRY(current->allocate_fd(rfd + 1));
@ -231,8 +210,8 @@ Result<u64> sys_pipe(Registers*, SyscallArgs args)
TRY(Pipe::create(rpipe, wpipe));
current->fd_table[rfd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(rpipe, O_RDONLY)), 0 };
current->fd_table[wfd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(wpipe, O_WRONLY)), 0 };
current->fd_table[rfd] = FileDescriptor { rpipe, 0, O_RDONLY };
current->fd_table[wfd] = FileDescriptor { wpipe, 0, O_WRONLY };
return 0;
}
@ -243,98 +222,9 @@ Result<u64> sys_umask(Registers*, SyscallArgs args)
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
mode_t old_umask = current->umask;
current->umask = new_umask & 0777;
return old_umask;
}
Result<u64> sys_truncate(Registers*, SyscallArgs args)
{
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
size_t length = (size_t)args[1];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_wpath));
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
TRY(inode->truncate(length));
return 0;
}
Result<u64> sys_ftruncate(Registers*, SyscallArgs args)
{
int fd = (int)args[0];
size_t length = (size_t)args[1];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto description = TRY(current->resolve_fd(fd))->description;
if (!(description->flags & O_WRONLY)) return err(EBADF);
TRY(description->inode->truncate(length));
return 0;
}
Result<u64> sys_utimensat(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
const auto* times = (const struct timespec*)args[2];
int flags = (int)args[3];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_fattr));
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
struct timespec ktimes[2];
ktimes[0].tv_sec = ktimes[1].tv_sec = 0;
ktimes[0].tv_nsec = ktimes[1].tv_nsec = UTIME_NOW;
if (times && !MemoryManager::copy_from_user(times, ktimes, sizeof(ktimes))) return err(EFAULT);
// No permission checks are performed, since no actual modification is done, but the above checks are still
// performed.
if (ktimes[0].tv_nsec == UTIME_OMIT && ktimes[1].tv_nsec == UTIME_OMIT) return 0;
bool allow_write_access = ktimes[0].tv_nsec == UTIME_NOW && ktimes[1].tv_nsec == UTIME_NOW;
if (allow_write_access)
{
if (!VFS::can_write(inode, current->auth) && current->auth.euid != inode->metadata().uid &&
current->auth.euid != 0)
return err(EACCES);
}
else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
return err(EPERM);
auto metadata = inode->metadata();
if (ktimes[0].tv_nsec != UTIME_OMIT)
{
if (ktimes[0].tv_nsec == UTIME_NOW) metadata.atime = *Timer::realtime_clock();
else
{
if (ktimes[0].tv_nsec < 0 || ktimes[0].tv_nsec > 999'999'999) return err(EINVAL);
metadata.atime = ktimes[0];
}
}
if (ktimes[1].tv_nsec != UTIME_OMIT)
{
if (ktimes[1].tv_nsec == UTIME_NOW) metadata.mtime = *Timer::realtime_clock();
else
{
if (ktimes[1].tv_nsec < 0 || ktimes[1].tv_nsec > 999'999'999) return err(EINVAL);
metadata.mtime = ktimes[1];
}
}
TRY(inode->set_metadata(metadata));
return 0;
}

View File

@ -1,4 +1,3 @@
#include "Pledge.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
@ -12,23 +11,22 @@ Result<u64> sys_getdents(Registers*, SyscallArgs args)
usize count = (usize)args[2];
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto& descriptor = *TRY(current->resolve_fd(fd));
if (descriptor.inode()->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (descriptor.inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
usize nwrite = 0;
while (nwrite < count)
{
VFS::DirectoryEntry entry;
bool ok = descriptor.inode()->get(descriptor.offset).try_set_value(entry);
bool ok = descriptor.inode->get(descriptor.offset).try_set_value(entry);
if (!ok) break;
descriptor.offset++;
luna_dirent kent;
kent.inode = entry.inode->metadata().inum;
kent.inode = entry.inode->inode_number();
strlcpy(kent.name, entry.name.chars(), entry.name.length() + 1);
if (!MemoryManager::copy_to_user_typed(ent, &kent)) return err(EFAULT);

View File

@ -1,4 +1,3 @@
#include "Pledge.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
@ -6,54 +5,41 @@
Result<u64> sys_getpid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
return current->id;
return Scheduler::current()->id;
}
Result<u64> sys_getppid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
auto* parent = current->parent;
auto* parent = Scheduler::current()->parent;
return parent ? parent->id : 0;
}
Result<u64> sys_getuid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
return current->auth.uid;
return Scheduler::current()->auth.uid;
}
Result<u64> sys_geteuid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
return current->auth.euid;
return Scheduler::current()->auth.euid;
}
Result<u64> sys_getgid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
return current->auth.gid;
return Scheduler::current()->auth.gid;
}
Result<u64> sys_getegid(Registers*, SyscallArgs)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
return current->auth.egid;
return Scheduler::current()->auth.egid;
}
Result<u64> sys_setuid(Registers*, SyscallArgs args)
{
u32 uid = (u32)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_id));
Credentials& auth = current->auth;
Credentials& auth = Scheduler::current()->auth;
if (auth.euid == 0)
{
@ -71,9 +57,7 @@ Result<u64> sys_seteuid(Registers*, SyscallArgs args)
{
u32 uid = (u32)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_id));
Credentials& auth = current->auth;
Credentials& auth = Scheduler::current()->auth;
if (auth.euid != 0 && uid != auth.uid && uid != auth.suid) return err(EPERM);
auth.euid = uid;
@ -85,9 +69,7 @@ Result<u64> sys_setgid(Registers*, SyscallArgs args)
{
u32 gid = (u32)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_id));
Credentials& auth = current->auth;
Credentials& auth = Scheduler::current()->auth;
if (auth.euid == 0)
{
@ -105,9 +87,7 @@ Result<u64> sys_setegid(Registers*, SyscallArgs args)
{
u32 gid = (u32)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_id));
Credentials& auth = current->auth;
Credentials& auth = Scheduler::current()->auth;
if (auth.euid != 0 && gid != auth.gid && gid != auth.sgid) return err(EPERM);
auth.egid = gid;
@ -121,9 +101,8 @@ Result<u64> sys_setpgid(Registers*, SyscallArgs args)
pid_t pgid = (pid_t)args[1];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_proc));
if (pid == 0) pid = current->id;
if (pgid == 0) pgid = current->id;
if (pid == 0) pid = (pid_t)current->id;
if (pgid == 0) pgid = (pid_t)current->id;
if (pgid < 0) return err(EINVAL);
@ -134,7 +113,7 @@ Result<u64> sys_setpgid(Registers*, SyscallArgs args)
if (thread->has_called_exec) return err(EPERM);
if (pgid != current->id)
if (pgid != (pid_t)current->id)
{
bool pgid_exists = false;
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
@ -154,8 +133,7 @@ Result<u64> sys_getpgid(Registers*, SyscallArgs args)
pid_t pid = (pid_t)args[0];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
if (pid == 0) pid = current->id;
if (pid == 0) pid = (pid_t)current->id;
if (pid < 0) return err(EINVAL);
@ -172,15 +150,12 @@ Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
int flags = (int)args[3];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_wpath));
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
if (current->auth.euid != 0 && current->auth.euid != inode->metadata().uid) return err(EPERM);
if (current->auth.euid != 0 && current->auth.euid != inode->uid()) return err(EPERM);
auto metadata = inode->metadata();
metadata.mode = mode;
TRY(inode->set_metadata(metadata));
TRY(inode->chmod(mode));
return 0;
}
@ -189,21 +164,17 @@ Result<u64> sys_fchownat(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
uid_t uid = (u32)args[2];
gid_t gid = (u32)args[3];
u32 uid = (u32)args[2];
u32 gid = (u32)args[3];
int flags = (int)args[4];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_chown));
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
if (current->auth.euid != 0) return err(EPERM);
auto metadata = inode->metadata();
if (uid != (uid_t)-1) metadata.uid = uid;
if (gid != (gid_t)-1) metadata.gid = gid;
TRY(inode->set_metadata(metadata));
TRY(inode->chown(uid == (u32)-1 ? inode->uid() : uid, gid == (u32)-1 ? inode->gid() : gid));
return 0;
}

View File

@ -1,5 +1,4 @@
#include "Log.h"
#include "Pledge.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
@ -13,7 +12,6 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
int flags = (int)args[2];
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_cpath));
auto dirname = TRY(PathParser::dirname(path.view()));
auto basename = TRY(PathParser::basename(path.view()));
@ -28,8 +26,8 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
auto child = TRY(inode->find(basename.chars()));
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (current->auth.euid != 0 && VFS::is_sticky(inode) && current->auth.euid != inode->metadata().uid &&
current->auth.euid != child->metadata().uid)
if (current->auth.euid != 0 && VFS::is_sticky(inode) && current->auth.euid != inode->uid() &&
current->auth.euid != child->uid())
return err(EACCES);
TRY(inode->remove_entry(basename.chars()));
@ -46,7 +44,6 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
if (target.is_empty()) return err(ENOENT);
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_cpath));
auto parent = TRY(PathParser::dirname(linkpath.view()));
@ -59,10 +56,7 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
TRY(VFS::validate_filename(child_name.view()));
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
auto metadata = inode->metadata();
metadata.uid = current->auth.euid;
metadata.gid = current->auth.egid;
TRY(inode->set_metadata(metadata));
TRY(inode->chown(current->auth.euid, current->auth.egid));
TRY(parent_inode->add_entry(inode, child_name.chars()));
return 0;
@ -76,7 +70,6 @@ Result<u64> sys_readlinkat(Registers*, SyscallArgs args)
usize bufsiz = (usize)args[3];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_rpath));
auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false));
@ -88,6 +81,8 @@ Result<u64> sys_readlinkat(Registers*, SyscallArgs args)
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;
@ -102,7 +97,6 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
int flags = (int)args[4];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_cpath));
auto parent = TRY(PathParser::dirname(newpath.view()));

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