Compare commits
195 Commits
eeb9e16a74
...
e029679fba
Author | SHA1 | Date | |
---|---|---|---|
e029679fba | |||
15f4f7c72f | |||
0002c2314c | |||
27eacac19c | |||
c5e11bb6cf | |||
3c9b2c49aa | |||
1528c772fd | |||
39ba4c9087 | |||
c524dc8d58 | |||
e76a91d5d0 | |||
cb0d5cb6a1 | |||
06aa7725a1 | |||
64f9e9dcde | |||
9c912ddc51 | |||
419604a4d2 | |||
97037b06cb | |||
c2f173f584 | |||
a772d92e6f | |||
6f3ed70363 | |||
d48142f163 | |||
cbea66c533 | |||
c6d817a0fd | |||
55c362eecf | |||
9fd8b10b3f | |||
516d6bc65e | |||
4becb2e427 | |||
3e174337ab | |||
0f377e7289 | |||
798a6d63aa | |||
3f1e6fc2ff | |||
b8ae61b7c7 | |||
1449e966ab | |||
24f9dd22ec | |||
b8e70996c3 | |||
6058a69182 | |||
e247310ded | |||
a35ca0b367 | |||
d144a818d8 | |||
33c1a9c92b | |||
65834ff491 | |||
5c2718545f | |||
e5905a33e1 | |||
993e94cc76 | |||
746834e2d9 | |||
ce3542e2bd | |||
e72bc55c6f | |||
2abb43d709 | |||
70a232cfcd | |||
30ff704342 | |||
6e69d37d62 | |||
f2a5c9ad3f | |||
95a33c484e | |||
4a654bf093 | |||
d43590e68c | |||
c1f4997448 | |||
7b88b9cea3 | |||
54cc80f649 | |||
b8f81502b8 | |||
e8e05159c1 | |||
49a6c39c38 | |||
706752d6b9 | |||
1e68ac7312 | |||
6761b3fcaf | |||
71d1084be2 | |||
f65deb727a | |||
d07f592306 | |||
cad0bd8c48 | |||
db3151d93b | |||
ba3e32917e | |||
cfb60fad25 | |||
9954fc1658 | |||
a98df9e743 | |||
e2a77bb3da | |||
0ae409ae22 | |||
181b4c151b | |||
0c64b6e040 | |||
fb3c31907d | |||
52064e0317 | |||
ec3c1132d2 | |||
5ea73197ad | |||
5a1adcb2a6 | |||
c4f6191e24 | |||
39e4fbd112 | |||
32fd6889b9 | |||
c6a5a81a7a | |||
3f55a70f6e | |||
b1e164f360 | |||
ed8b210639 | |||
16590dbb02 | |||
919c71ff85 | |||
1caa2c0888 | |||
8748364b7e | |||
49662b6069 | |||
d96ff92461 | |||
bfb76b5625 | |||
8c13513bf4 | |||
37e9b25b62 | |||
a92077d311 | |||
1481a4736a | |||
4195e7f206 | |||
159c05c064 | |||
b63a8ff245 | |||
917203ef11 | |||
826be882a9 | |||
198935eb30 | |||
7c254e5e15 | |||
2e63b93e48 | |||
77ebdda2e0 | |||
097353e779 | |||
10c892d606 | |||
f45734c61d | |||
bc20e1a31b | |||
b01aa72f17 | |||
d41fb85466 | |||
5aa042a5f2 | |||
f150425222 | |||
842b212685 | |||
641b65da0f | |||
bfcca3a220 | |||
b5d146b492 | |||
b447c1a261 | |||
f8e86b3b01 | |||
d8f6af99b8 | |||
c5e24e478f | |||
53d9f5c6fc | |||
84c1ac4cee | |||
7293d47bf0 | |||
f66b0497cf | |||
2572695c8d | |||
9443551d71 | |||
b4527786d4 | |||
3aaf1c5d84 | |||
4794d0dfef | |||
9c1e275f34 | |||
6593f9241b | |||
df4227eab8 | |||
f8cb6e03df | |||
207d901de8 | |||
df77fc8de8 | |||
b1fb6dee8a | |||
aac8280e8a | |||
d0ceec6952 | |||
7983d63b8e | |||
2198dedb96 | |||
cd9219df52 | |||
e8f3dd4cf9 | |||
6c26236167 | |||
b17793134e | |||
dc35c42371 | |||
4c87d72b44 | |||
d01087362e | |||
3598dacbed | |||
3638d3da46 | |||
1c76675e40 | |||
6e269c6bc4 | |||
43fa128e29 | |||
8475a3aad9 | |||
187f0ff83e | |||
0c873923e8 | |||
63745565db | |||
7b24a4d1c6 | |||
8d3b3aaf05 | |||
bb3127c212 | |||
cca806f088 | |||
7a7ae086f5 | |||
fb08594a18 | |||
9303c44269 | |||
a12b018b03 | |||
c4e30c3029 | |||
c1d08b904e | |||
200bb6c240 | |||
6b0bc66fd2 | |||
4ed7ec5e93 | |||
28cc4b2306 | |||
11df5a2ec3 | |||
5aad7d3154 | |||
58fa297068 | |||
28dd8194af | |||
b88da4811f | |||
7f990b161b | |||
9bb3fed611 | |||
105ed79f8f | |||
905e71527e | |||
c6d91c89cd | |||
2e2b87b714 | |||
b12f42cfe2 | |||
c70790bf62 | |||
a990cc145e | |||
b0d7870ec6 | |||
9a00b29304 | |||
033aff4f6c | |||
c5af1bcef9 | |||
b3c478f19e | |||
ad3f3bf4db | |||
01359af288 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,5 +5,8 @@ initrd/boot/moon
|
||||
env-local.sh
|
||||
initrd/bin/**
|
||||
base/usr/**
|
||||
base/etc/skel/LICENSE
|
||||
.fakeroot
|
||||
kernel/config.cmake
|
||||
ports/out/
|
||||
ports/temp/
|
||||
|
@ -5,7 +5,7 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING true)
|
||||
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.4.0)
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.5.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
|
||||
-Wredundant-decls -Wshadow -Wconversion -Wbidi-chars=any
|
||||
-fno-asynchronous-unwind-tables -fno-omit-frame-pointer
|
||||
-std=c++20 -fno-rtti -fno-exceptions)
|
||||
|
||||
|
19
README.md
19
README.md
@ -1,5 +1,5 @@
|
||||
# Luna
|
||||
A very basic POSIX-based operating system for personal computers, written in C++. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
|
||||
A 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)
|
||||
|
||||
## Another UNIX clone?
|
||||
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
|
||||
@ -8,15 +8,18 @@ A very basic POSIX-based operating system for personal computers, written in C++
|
||||
- x86_64-compatible lightweight [kernel](kernel/).
|
||||
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/).
|
||||
- [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/thread/ELF.cpp) from the file system as userspace tasks.
|
||||
- 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).
|
||||
- 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 mostly POSIX-compatible.
|
||||
- [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.
|
||||
- POSIX [signal](libc/src/signal.cpp) support.
|
||||
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
||||
- Designed around [UTF-8](libluna/include/luna/Utf8.h).
|
||||
- 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.
|
||||
- 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.
|
||||
- Build system uses [CMake](CMakeLists.txt).
|
||||
- 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/).
|
||||
|
||||
## Setup
|
||||
|
||||
@ -50,7 +53,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?
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
## License
|
||||
Luna is open-source and free software under the [BSD-2 License](LICENSE).
|
||||
Luna is open-source and free software under the [BSD-2-Clause License](LICENSE).
|
||||
|
@ -28,8 +28,6 @@ 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)
|
||||
@ -37,8 +35,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(buffer-test.cpp buffer-test)
|
||||
luna_app(touch.cpp touch)
|
||||
luna_app(free.cpp free)
|
||||
|
@ -1,27 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
#include <luna/String.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/Prompt.h>
|
||||
|
||||
using os::File;
|
||||
|
||||
@ -14,10 +16,20 @@ 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());
|
||||
|
50
apps/free.cpp
Normal file
50
apps/free.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#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;
|
||||
}
|
35
apps/gol.cpp
35
apps/gol.cpp
@ -6,6 +6,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -24,6 +25,7 @@ static int g_fb_height;
|
||||
static int g_fd;
|
||||
|
||||
static Cell* g_cells;
|
||||
static char* g_fb;
|
||||
|
||||
static Result<void> fill_cells()
|
||||
{
|
||||
@ -47,27 +49,29 @@ 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++)
|
||||
{
|
||||
auto& cell = find_cell(i, j);
|
||||
if (cell.state) memset(g_buf + (j * CELL_WIDTH * BYTES_PER_PIXEL), 0xff, CELL_WIDTH * BYTES_PER_PIXEL);
|
||||
}
|
||||
char* buf = g_fb + (i * g_fb_width * CELL_HEIGHT * BYTES_PER_PIXEL);
|
||||
|
||||
for (int j = 0; j < CELL_HEIGHT; j++) { write(g_fd, g_buf, g_fb_width * 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msync(g_fb, g_fb_height * g_fb_width * BYTES_PER_PIXEL, MS_SYNC);
|
||||
}
|
||||
|
||||
static int find_neighbors(int row, int column)
|
||||
@ -137,7 +141,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
else
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
g_fd = open("/dev/fb0", O_WRONLY);
|
||||
g_fd = open("/dev/fb0", O_RDWR);
|
||||
if (g_fd < 0)
|
||||
{
|
||||
perror("gol: cannot open framebuffer for writing");
|
||||
@ -149,7 +153,13 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
TRY(fill_cells());
|
||||
|
||||
g_buf = (char*)TRY(calloc_impl(g_fb_width, BYTES_PER_PIXEL));
|
||||
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;
|
||||
}
|
||||
|
||||
draw_cells();
|
||||
|
||||
@ -162,5 +172,8 @@ 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;
|
||||
}
|
||||
|
186
apps/init.cpp
186
apps/init.cpp
@ -3,10 +3,14 @@
|
||||
#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>
|
||||
@ -17,20 +21,10 @@
|
||||
#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;
|
||||
@ -40,6 +34,8 @@ 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 {};
|
||||
};
|
||||
@ -56,6 +52,20 @@ 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)
|
||||
{
|
||||
@ -65,6 +75,12 @@ 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
|
||||
{
|
||||
@ -175,28 +191,10 @@ 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)
|
||||
@ -232,6 +230,15 @@ 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)
|
||||
@ -254,7 +261,7 @@ static Result<void> load_service(const os::Path& path)
|
||||
|
||||
if (service.command.is_empty())
|
||||
{
|
||||
do_log("[init] service file is missing 'Command' or 'Script' entry, aborting!\n");
|
||||
do_log("[init] service file is missing 'Command' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -265,9 +272,9 @@ static Result<void> load_service(const os::Path& path)
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> load_services()
|
||||
static Result<void> load_services(StringView path)
|
||||
{
|
||||
auto dir = TRY(os::Directory::open("/etc/init"));
|
||||
auto dir = TRY(os::Directory::open(path));
|
||||
|
||||
auto services = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
|
||||
sort(services.begin(), services.end(), String::compare);
|
||||
@ -277,9 +284,9 @@ static Result<void> load_services()
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> start_services()
|
||||
static Result<void> start_services(StringView path)
|
||||
{
|
||||
TRY(load_services());
|
||||
TRY(load_services(path));
|
||||
for (auto& service : g_services)
|
||||
{
|
||||
do_log("[init] starting service %s\n", service.name.chars());
|
||||
@ -296,6 +303,19 @@ 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());
|
||||
@ -310,7 +330,16 @@ static void mount_tmpfs()
|
||||
if (chmod("/tmp", 01777) < 0) exit(255);
|
||||
}
|
||||
|
||||
int main()
|
||||
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)
|
||||
{
|
||||
if (getpid() != 1)
|
||||
{
|
||||
@ -318,25 +347,35 @@ int main()
|
||||
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+");
|
||||
g_init_log = fopen("/dev/uart0", "w");
|
||||
check(g_init_log);
|
||||
setlinebuf(g_init_log);
|
||||
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");
|
||||
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT");
|
||||
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");
|
||||
|
||||
start_services();
|
||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
||||
|
||||
if (path.is_empty()) path = "/etc/init";
|
||||
start_services(path);
|
||||
|
||||
while (1)
|
||||
{
|
||||
@ -371,3 +410,70 @@ int main()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int pfds[2];
|
||||
|
||||
if (pipe(pfds) < 0)
|
||||
{
|
||||
perror("pipe");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0)
|
||||
{
|
||||
close(pfds[1]);
|
||||
|
||||
char buffer[4096];
|
||||
size_t nread = read(pfds[0], buffer, sizeof(buffer) - 1);
|
||||
buffer[nread] = 0;
|
||||
close(pfds[0]);
|
||||
|
||||
puts(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (child == -1)
|
||||
{
|
||||
perror("fork");
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(pfds[0]);
|
||||
|
||||
const char* string = "Hello from a child process who just received this message from its parent!";
|
||||
write(pfds[1], string, strlen(string));
|
||||
close(pfds[1]);
|
||||
|
||||
wait(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
@ -26,11 +26,12 @@ 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();
|
||||
|
||||
@ -44,7 +45,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
username = name.view();
|
||||
}
|
||||
|
||||
execl("/bin/su", "login", "-lp", "--", username.chars(), nullptr);
|
||||
execl("/usr/bin/su", "login", "-lp", "--", username.chars(), nullptr);
|
||||
|
||||
perror("su");
|
||||
return 1;
|
||||
|
66
apps/ls.cpp
66
apps/ls.cpp
@ -8,6 +8,7 @@
|
||||
#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)
|
||||
{
|
||||
@ -47,18 +48,46 @@ int sort_reverse(const os::Directory::Entry* a, const os::Directory::Entry* b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result<String> entry_join(const Vector<os::Directory::Entry>& vec, StringView delim)
|
||||
#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)
|
||||
{
|
||||
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();
|
||||
@ -75,6 +104,7 @@ 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" };
|
||||
|
||||
@ -98,6 +128,8 @@ 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;
|
||||
@ -154,11 +186,13 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
if (!long_list)
|
||||
{
|
||||
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv));
|
||||
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv, !no_colors && isatty(STDIN_FILENO)));
|
||||
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;
|
||||
@ -176,14 +210,32 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
if (!human_readable && !si)
|
||||
{
|
||||
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
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());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
|
||||
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -72,13 +72,9 @@ int main()
|
||||
// Now, mount the /dev file system on the new root.
|
||||
mount_devfs();
|
||||
|
||||
/*setenv("PATH", "/bin:/usr/bin", 1);
|
||||
char* argv[] = { "init", nullptr };
|
||||
char* envp[] = { nullptr };
|
||||
execvpe(argv[0], argv, envp);*/
|
||||
|
||||
char* argv[] = { "/usr/bin/init", nullptr };
|
||||
execv(argv[0], argv);
|
||||
char* envp[] = { nullptr };
|
||||
execve(argv[0], argv, envp);
|
||||
|
||||
fail_errno("Failed to execute init");
|
||||
|
||||
|
30
apps/rm.cpp
30
apps/rm.cpp
@ -1,10 +1,35 @@
|
||||
#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);
|
||||
@ -12,11 +37,12 @@ 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(os::FileSystem::remove(path));
|
||||
if (!recursive) TRY(remove_wrapper(path, verbose));
|
||||
else
|
||||
TRY(os::FileSystem::remove_tree(path));
|
||||
TRY(remove_tree(path, verbose));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/Mode.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.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 status.");
|
||||
parser.add_description("Display file metadata.");
|
||||
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,10 +36,20 @@ Result<int> luna_main(int argc, char** argv)
|
||||
char buf[11];
|
||||
os::format_mode(st.st_mode, buf);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
chdir(entry->pw_dir);
|
||||
clearenv();
|
||||
setenv("PATH", "/bin:/sbin", 1);
|
||||
setenv("PATH", "/usr/bin:/usr/local/bin", 1);
|
||||
setpgid(0, 0);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <luna/Syscall.h>
|
||||
#include <moon/Syscall.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
47
apps/touch.cpp
Normal file
47
apps/touch.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#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;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
Name=mount-home
|
||||
Description=Mount the user's home directory on a writable filesystem.
|
||||
Script=/etc/startup/mount-home.sh
|
||||
Command=/etc/startup/mount-home.sh
|
||||
Wait=true
|
||||
|
@ -1,2 +1,2 @@
|
||||
root:toor:0:0:Administrator:/:/bin/sh
|
||||
selene:moon:1000:1000:User:/home/selene:/bin/sh
|
||||
root:toor:0:0:Administrator:/:/usr/bin/sh
|
||||
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
|
||||
|
14
base/etc/skel/welcome
Normal file
14
base/etc/skel/welcome
Normal file
@ -0,0 +1,14 @@
|
||||
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.
|
@ -1,2 +1,6 @@
|
||||
#!/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/
|
||||
|
@ -6,6 +6,7 @@ set(SOURCES
|
||||
${HEADERS}
|
||||
src/main.cpp
|
||||
src/Log.cpp
|
||||
src/Pledge.cpp
|
||||
src/cxxabi.cpp
|
||||
src/video/Framebuffer.cpp
|
||||
src/video/TextConsole.cpp
|
||||
@ -14,6 +15,7 @@ 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
|
||||
@ -41,24 +43,36 @@ 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/thread/ELF.cpp
|
||||
src/binfmt/ELF.cpp
|
||||
src/binfmt/BinaryFormat.cpp
|
||||
src/binfmt/Script.cpp
|
||||
)
|
||||
|
||||
if("${LUNA_ARCH}" MATCHES "x86_64")
|
||||
@ -76,6 +90,7 @@ 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()
|
||||
|
||||
|
@ -15,3 +15,7 @@
|
||||
# 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)
|
||||
|
@ -11,4 +11,5 @@ 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)
|
||||
|
@ -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();
|
||||
|
69
kernel/src/Pledge.cpp
Normal file
69
kernel/src/Pledge.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#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;
|
||||
}
|
19
kernel/src/Pledge.h
Normal file
19
kernel/src/Pledge.h
Normal file
@ -0,0 +1,19 @@
|
||||
#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);
|
131
kernel/src/api/Keyboard.h
Normal file
131
kernel/src/api/Keyboard.h
Normal file
@ -0,0 +1,131 @@
|
||||
#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);
|
||||
}
|
22
kernel/src/api/Mouse.h
Normal file
22
kernel/src/api/Mouse.h
Normal file
@ -0,0 +1,22 @@
|
||||
#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);
|
||||
}
|
@ -7,7 +7,8 @@
|
||||
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
|
||||
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
|
||||
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
|
||||
_e(getpgid)
|
||||
_e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \
|
||||
_e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat)
|
||||
|
||||
enum Syscalls
|
||||
{
|
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#include "api/Keyboard.h"
|
||||
#include <luna/Option.h>
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
struct KeyboardState
|
||||
struct TTYKeyboardState
|
||||
{
|
||||
bool ignore_next { false };
|
||||
bool left_shift { false };
|
||||
@ -12,5 +13,12 @@ namespace Keyboard
|
||||
bool capslock { false };
|
||||
};
|
||||
|
||||
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state);
|
||||
struct KeyboardState
|
||||
{
|
||||
bool ignore_next { false };
|
||||
};
|
||||
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state);
|
||||
|
||||
Option<moon::KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -1,10 +1,12 @@
|
||||
#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"
|
||||
@ -27,8 +29,9 @@ 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();
|
||||
|
||||
static Thread* g_io_thread;
|
||||
Thread* g_io_thread;
|
||||
|
||||
typedef void (*interrupt_handler_t)(Registers*, void*);
|
||||
|
||||
@ -134,27 +137,35 @@ 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: FIXME_UNHANDLED_INTERRUPT("Reserved exception or #DF/#MC, which shouldn't call handle_x86_exception");
|
||||
default: fail("Caught invalid 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;
|
||||
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
|
||||
moon::MousePacket packet;
|
||||
|
||||
ConsoleDevice::did_press_or_release_key(scancode);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_interrupt(Registers* regs, void*)
|
||||
{
|
||||
Timer::tick();
|
||||
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
||||
if (should_invoke_scheduler())
|
||||
{
|
||||
Scheduler::invoke(regs);
|
||||
TextConsole::tick_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_interrupt(Registers*, void*)
|
||||
@ -261,6 +272,8 @@ namespace CPU
|
||||
|
||||
remap_pic();
|
||||
|
||||
init_mouse();
|
||||
|
||||
sync_interrupts();
|
||||
}
|
||||
|
||||
@ -326,7 +339,10 @@ namespace CPU
|
||||
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
|
||||
{
|
||||
StackFrame* current_frame = (StackFrame*)base_pointer;
|
||||
while (current_frame && (u64)current_frame >= 0xFFFF'FFFF'8000'0000 &&
|
||||
while (current_frame &&
|
||||
#ifndef MOON_ENABLE_USERSPACE_STACK_TRACES
|
||||
(u64)current_frame >= 0xFFFF'FFFF'8000'0000 &&
|
||||
#endif
|
||||
MemoryManager::validate_access(current_frame, sizeof(*current_frame), MemoryManager::DEFAULT_ACCESS) &&
|
||||
current_frame->instruction)
|
||||
{
|
||||
|
@ -19,21 +19,20 @@ 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 == TAB || scancode == F11 || scancode == F12;
|
||||
return (scancode > 0x3A && scancode < 0x47) || scancode == F11 || scancode == F12;
|
||||
}
|
||||
|
||||
constexpr char key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\0', // tab
|
||||
'\t', // tab
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
'\0', // left ctrl
|
||||
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
|
||||
@ -75,7 +74,7 @@ constexpr char shifted_key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||
'\0', // tab
|
||||
'\t', // tab
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||
'\0', // left ctrl
|
||||
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
|
||||
@ -113,7 +112,71 @@ constexpr char shifted_key_table[] = {
|
||||
'.', // keypad .
|
||||
};
|
||||
|
||||
static bool is_shifted(const Keyboard::KeyboardState& state)
|
||||
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)
|
||||
{
|
||||
if (state.capslock) return !(state.left_shift || state.right_shift);
|
||||
return state.left_shift || state.right_shift;
|
||||
@ -121,7 +184,7 @@ static bool is_shifted(const Keyboard::KeyboardState& state)
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
Option<char> decode_scancode_tty(u8 scancode, KeyboardState& state)
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state)
|
||||
{
|
||||
if (state.ignore_next)
|
||||
{
|
||||
@ -184,4 +247,24 @@ 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 };
|
||||
}
|
||||
}
|
||||
|
@ -110,10 +110,11 @@ namespace MMU
|
||||
return result;
|
||||
}
|
||||
|
||||
PageTableEntry& l4_entry(u64 virt)
|
||||
PageTableEntry& l4_entry(u64 virt, PageDirectory* directory = nullptr)
|
||||
{
|
||||
auto index = l4_index(virt);
|
||||
return get_virtual_page_directory()->entries[index];
|
||||
auto* vdir = directory ? translate_physical(directory) : get_virtual_page_directory();
|
||||
return vdir->entries[index];
|
||||
}
|
||||
|
||||
PageDirectory& page_table(const PageTableEntry& entry)
|
||||
@ -139,9 +140,9 @@ namespace MMU
|
||||
return page_table(entry).entries[index];
|
||||
}
|
||||
|
||||
Result<PageTableEntry*> find_entry(u64 virt)
|
||||
Result<PageTableEntry*> find_entry(u64 virt, PageDirectory* directory = nullptr)
|
||||
{
|
||||
const auto& l4 = l4_entry(virt);
|
||||
const auto& l4 = l4_entry(virt, directory);
|
||||
if (!l4.present) return err(EFAULT);
|
||||
auto& l3 = l3_entry(l4, virt);
|
||||
if (!l3.present) return err(EFAULT);
|
||||
@ -152,9 +153,9 @@ namespace MMU
|
||||
return &l1_entry(l2, virt);
|
||||
}
|
||||
|
||||
Result<PageTableEntry*> apply_cascading_flags(u64 virt, int flags)
|
||||
Result<PageTableEntry*> apply_cascading_flags(u64 virt, int flags, PageDirectory* directory = nullptr)
|
||||
{
|
||||
auto& l4 = l4_entry(virt);
|
||||
auto& l4 = l4_entry(virt, directory);
|
||||
if (!l4.present) return err(EFAULT);
|
||||
if (flags & Flags::ReadWrite) l4.read_write = true;
|
||||
if (flags & Flags::User) l4.user = true;
|
||||
@ -183,9 +184,9 @@ namespace MMU
|
||||
entry.set_address(phys);
|
||||
}
|
||||
|
||||
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages)
|
||||
Result<void> map(u64 virt, u64 phys, int flags, UseHugePages use_huge_pages, PageDirectory* directory)
|
||||
{
|
||||
auto& l4 = l4_entry(virt);
|
||||
auto& l4 = l4_entry(virt, directory);
|
||||
if (!l4.present)
|
||||
{
|
||||
const u64 addr = TRY(MemoryManager::alloc_frame());
|
||||
@ -252,9 +253,9 @@ namespace MMU
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<u64> unmap(u64 virt)
|
||||
Result<u64> unmap(u64 virt, PageDirectory* directory)
|
||||
{
|
||||
auto& l1 = *TRY(find_entry(virt));
|
||||
auto& l1 = *TRY(find_entry(virt, directory));
|
||||
if (!l1.present) return err(EFAULT);
|
||||
const u64 address = l1.get_address();
|
||||
l1.clear();
|
||||
@ -262,16 +263,16 @@ namespace MMU
|
||||
return address;
|
||||
}
|
||||
|
||||
Result<u64> get_physical(u64 virt)
|
||||
Result<u64> get_physical(u64 virt, PageDirectory* directory)
|
||||
{
|
||||
const auto& l1 = *TRY(find_entry(virt));
|
||||
const auto& l1 = *TRY(find_entry(virt, directory));
|
||||
if (!l1.present) return err(EFAULT);
|
||||
return l1.get_address();
|
||||
}
|
||||
|
||||
Result<int> get_flags(u64 virt)
|
||||
Result<int> get_flags(u64 virt, PageDirectory* directory)
|
||||
{
|
||||
const auto& l1 = *TRY(find_entry(virt));
|
||||
const auto& l1 = *TRY(find_entry(virt, directory));
|
||||
if (!l1.present) return err(EFAULT);
|
||||
return arch_flags_to_mmu(l1);
|
||||
}
|
||||
@ -440,12 +441,8 @@ 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.set_address(TRY(MemoryManager::alloc_frame()));
|
||||
|
||||
memcpy(&page_table(new_l1), &page_table(old_l1), ARCH_PAGE_SIZE);
|
||||
new_l1.present = false; // The entry must be filled in by the caller.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
118
kernel/src/arch/x86_64/PS2Mouse.cpp
Normal file
118
kernel/src/arch/x86_64/PS2Mouse.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#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;
|
||||
}
|
@ -106,7 +106,7 @@ bool Thread::deliver_signal(int signo, Registers* current_regs)
|
||||
|
||||
memcpy(®s, current_regs, sizeof(regs));
|
||||
|
||||
kinfoln("signal: delivering signal %d for thread %ld, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id,
|
||||
kinfoln("signal: delivering signal %d for thread %d, 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 |= (1 << (signo - 1));
|
||||
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask.set(signo - 1, true);
|
||||
|
||||
rsp = regs.rsp;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#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>
|
||||
@ -161,7 +162,7 @@ namespace ATA
|
||||
{
|
||||
if (!(read_bm(BusmasterRegister::Status) & BMS_IRQPending)) return;
|
||||
|
||||
if (m_current_drive < 2 && m_drives[m_current_drive]) m_drives[m_current_drive]->irq_handler();
|
||||
if (m_current_drive < 2 && m_drives[m_current_drive].has_value()) m_drives[m_current_drive]->irq_handler();
|
||||
|
||||
m_irq_called = true;
|
||||
|
||||
@ -307,14 +308,7 @@ namespace ATA
|
||||
|
||||
kinfoln("ata: Channel %d has a drive on slot %d!", m_channel_index, 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();
|
||||
m_drives[drive] = Drive { this, drive, {} };
|
||||
|
||||
if (!m_drives[drive]->initialize())
|
||||
{
|
||||
@ -327,7 +321,7 @@ namespace ATA
|
||||
|
||||
for (u8 drive = 0; drive < 2; drive++)
|
||||
{
|
||||
if (m_drives[drive])
|
||||
if (m_drives[drive].has_value())
|
||||
{
|
||||
if (!m_drives[drive]->post_initialize())
|
||||
{
|
||||
@ -335,7 +329,7 @@ namespace ATA
|
||||
return false;
|
||||
}
|
||||
|
||||
auto rc = ATADevice::create(m_drives[drive]);
|
||||
auto rc = ATADevice::create(m_drives[drive].value_ptr());
|
||||
|
||||
if (rc.has_error())
|
||||
{
|
||||
@ -438,6 +432,18 @@ 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)
|
||||
{
|
||||
@ -723,7 +729,7 @@ namespace ATA
|
||||
|
||||
static u32 next_minor = 0;
|
||||
|
||||
Result<String> ATA::Drive::create_drive_name(SharedPtr<ATA::Drive> drive)
|
||||
Result<String> ATA::Drive::create_drive_name(ATA::Drive* drive)
|
||||
{
|
||||
static u32 cd_index = 0;
|
||||
static u32 sd_index = 0;
|
||||
@ -731,67 +737,28 @@ Result<String> ATA::Drive::create_drive_name(SharedPtr<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(SharedPtr<ATA::Drive> drive)
|
||||
Result<SharedPtr<Device>> ATADevice::create(ATA::Drive* drive)
|
||||
{
|
||||
auto device = TRY(adopt_shared_if_nonnull(new (std::nothrow) ATADevice()));
|
||||
device->m_drive = drive;
|
||||
auto device = TRY(adopt_shared_if_nonnull(new (std::nothrow) ATADevice(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;
|
||||
}
|
||||
|
||||
Result<u64> ATADevice::read(u8* buf, usize offset, usize size) const
|
||||
ATADevice::ATADevice(ATA::Drive* drive) : BlockDevice(drive->block_size(), drive->block_count()), m_drive(drive)
|
||||
{
|
||||
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());
|
||||
|
||||
// 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)
|
||||
if (buf.size() != m_drive->block_size())
|
||||
{
|
||||
// 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;
|
||||
kwarnln("ata: error while reading block %lu: cache entry size mismatch (%lu), data=%p", block, buf.size(),
|
||||
buf.data());
|
||||
fail("Cache entry size mismatch");
|
||||
}
|
||||
|
||||
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;
|
||||
return m_drive->read_lba(block, buf.data(), 1);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "arch/PCI.h"
|
||||
#include "fs/devices/BlockDevice.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include "lib/KMutex.h"
|
||||
#include <luna/Atomic.h>
|
||||
@ -164,7 +165,7 @@ namespace ATA
|
||||
|
||||
Result<void> read_lba(u64 lba, void* out, usize nblocks);
|
||||
|
||||
static Result<String> create_drive_name(SharedPtr<ATA::Drive> drive);
|
||||
static Result<String> create_drive_name(ATA::Drive* drive);
|
||||
|
||||
private:
|
||||
bool identify_ata();
|
||||
@ -256,7 +257,6 @@ 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;
|
||||
|
||||
SharedPtr<Drive> m_drives[2];
|
||||
Option<Drive> m_drives[2];
|
||||
};
|
||||
|
||||
class Controller
|
||||
class Controller : public Shareable
|
||||
{
|
||||
public:
|
||||
static Result<void> scan();
|
||||
@ -297,48 +297,29 @@ namespace ATA
|
||||
|
||||
}
|
||||
|
||||
class ATADevice : public Device
|
||||
class ATADevice : public BlockDevice
|
||||
{
|
||||
public:
|
||||
// Initializer for DeviceRegistry.
|
||||
static Result<SharedPtr<Device>> create(SharedPtr<ATA::Drive> drive);
|
||||
static Result<SharedPtr<Device>> create(ATA::Drive* drive);
|
||||
|
||||
Result<usize> read(u8*, usize, usize) const override;
|
||||
Result<void> read_block(Buffer& buf, u64 block) const override;
|
||||
|
||||
Result<usize> write(const u8*, usize, usize) override
|
||||
{
|
||||
return err(ENOTSUP);
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() 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;
|
||||
SharedPtr<ATA::Drive> m_drive;
|
||||
ATA::Drive* m_drive;
|
||||
String m_device_path;
|
||||
};
|
||||
|
42
kernel/src/binfmt/BinaryFormat.cpp
Normal file
42
kernel/src/binfmt/BinaryFormat.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#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)
|
||||
{
|
||||
}
|
114
kernel/src/binfmt/BinaryFormat.h
Normal file
114
kernel/src/binfmt/BinaryFormat.h
Normal file
@ -0,0 +1,114 @@
|
||||
#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);
|
||||
}
|
154
kernel/src/binfmt/ELF.cpp
Normal file
154
kernel/src/binfmt/ELF.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
#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));
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "binfmt/BinaryFormat.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/AddressSpace.h"
|
||||
#include <luna/SharedPtr.h>
|
||||
#include <luna/Types.h>
|
||||
|
||||
#define ELFMAG "\177ELF"
|
||||
@ -47,12 +49,20 @@ typedef struct
|
||||
u64 p_align; /* Segment alignment */
|
||||
} Elf64_Phdr;
|
||||
|
||||
struct ELFData
|
||||
class ELFLoader : public BinaryFormatLoader
|
||||
{
|
||||
u64 entry;
|
||||
};
|
||||
public:
|
||||
Result<bool> sniff() override;
|
||||
Result<u64> load(AddressSpace* space) override;
|
||||
|
||||
namespace ELFLoader
|
||||
{
|
||||
Result<ELFData> load(SharedPtr<VFS::Inode> inode, AddressSpace* space);
|
||||
Result<Vector<String>> cmdline(const String& path, Vector<String> args) override;
|
||||
|
||||
StringView format() const override
|
||||
{
|
||||
return "elf";
|
||||
}
|
||||
|
||||
ELFLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
|
||||
|
||||
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*, int);
|
||||
};
|
68
kernel/src/binfmt/Script.cpp
Normal file
68
kernel/src/binfmt/Script.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#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));
|
||||
}
|
25
kernel/src/binfmt/Script.h
Normal file
25
kernel/src/binfmt/Script.h
Normal file
@ -0,0 +1,25 @@
|
||||
#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;
|
||||
};
|
@ -41,8 +41,8 @@ namespace GPT
|
||||
|
||||
u32 partition_index = 1;
|
||||
|
||||
auto* table = TRY(make_array<PartitionEntry>(header.num_partitions));
|
||||
auto guard = make_scope_guard([table] { delete[] table; });
|
||||
auto* table = (PartitionEntry*)TRY(calloc_impl(header.num_partitions, sizeof(PartitionEntry)));
|
||||
auto guard = make_scope_guard([table] { free_impl(table); });
|
||||
|
||||
nread = TRY(device->read((u8*)table, partition_table_start, sizeof(PartitionEntry) * header.num_partitions));
|
||||
check(nread == sizeof(PartitionEntry) * header.num_partitions);
|
||||
@ -66,6 +66,9 @@ namespace GPT
|
||||
{
|
||||
header.checksum = 0;
|
||||
|
||||
return CRC32::checksum((u8*)&header, 0x5c);
|
||||
CRC32 crc;
|
||||
crc.append((u8*)&header, 0x5c);
|
||||
|
||||
return crc.digest();
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <bits/modes.h>
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/Common.h>
|
||||
|
||||
TarStream g_initrd;
|
||||
extern const BOOTBOOT bootboot;
|
||||
@ -18,17 +18,15 @@ void InitRD::initialize()
|
||||
g_initrd.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
|
||||
}
|
||||
|
||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode, uid_t uid, gid_t gid)
|
||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
||||
{
|
||||
auto rc = VFS::create_directory(path, Credentials {});
|
||||
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, 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 {};
|
||||
}
|
||||
|
||||
@ -39,19 +37,17 @@ Result<void> InitRD::populate_vfs()
|
||||
{
|
||||
if (entry.type == TarStream::EntryType::RegularFile)
|
||||
{
|
||||
auto file = TRY(VFS::create_file(entry.name.chars(), Credentials {}));
|
||||
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}));
|
||||
file->write(entry.data(), 0, entry.size);
|
||||
file->chmod(entry.mode & (mode_t)~S_IFMT);
|
||||
file->chown(entry.uid, entry.gid);
|
||||
}
|
||||
else if (entry.type == TarStream::EntryType::Directory)
|
||||
{
|
||||
TRY(vfs_create_dir_if_not_exists(entry.name.chars(), entry.mode, entry.uid, entry.gid));
|
||||
TRY(vfs_create_dir_if_not_exists(entry.name.chars(), entry.mode));
|
||||
}
|
||||
}
|
||||
|
||||
// Now we don't need the original initrd anymore
|
||||
MemoryManager::free_frames(bootboot.initrd_ptr, get_blocks_from_size(bootboot.initrd_size, ARCH_PAGE_SIZE));
|
||||
MemoryManager::free_frames(bootboot.initrd_ptr, ceil_div(bootboot.initrd_size, ARCH_PAGE_SIZE));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace MBR
|
||||
|
||||
Result<usize> write(const u8* buf, usize offset, usize length) override;
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -37,44 +37,19 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
|
||||
return err(EISDIR);
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
usize size() const override
|
||||
const VFS::InodeMetadata& metadata() const override
|
||||
{
|
||||
return 0;
|
||||
return m_mount_root_inode->metadata();
|
||||
}
|
||||
|
||||
mode_t mode() const override
|
||||
Result<void> set_metadata(const VFS::InodeMetadata& metadata) override
|
||||
{
|
||||
return m_mount_root_inode->mode();
|
||||
}
|
||||
|
||||
u32 uid() const override
|
||||
{
|
||||
return m_mount_root_inode->uid();
|
||||
}
|
||||
|
||||
u32 gid() const override
|
||||
{
|
||||
return m_mount_root_inode->gid();
|
||||
}
|
||||
|
||||
nlink_t nlinks() const override
|
||||
{
|
||||
return m_mount_root_inode->nlinks();
|
||||
}
|
||||
|
||||
Result<void> chmod(mode_t mode) override
|
||||
{
|
||||
return m_mount_root_inode->chmod(mode);
|
||||
}
|
||||
|
||||
Result<void> chown(u32 uid, u32 gid) override
|
||||
{
|
||||
return m_mount_root_inode->chown(uid, gid);
|
||||
return m_mount_root_inode->set_metadata(metadata);
|
||||
}
|
||||
|
||||
VFS::FileSystem* fs() const override
|
||||
@ -82,11 +57,6 @@ 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;
|
||||
@ -122,14 +92,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) override
|
||||
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override
|
||||
{
|
||||
return m_mount_root_inode->create_file(name);
|
||||
return m_mount_root_inode->create_file(name, mode);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
|
||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override
|
||||
{
|
||||
return m_mount_root_inode->create_subdirectory(name);
|
||||
return m_mount_root_inode->create_subdirectory(name, mode);
|
||||
}
|
||||
|
||||
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override
|
||||
|
@ -14,8 +14,14 @@ 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->chown(auth.euid, auth.egid);
|
||||
reader->chown(auth.euid, auth.egid);
|
||||
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;
|
||||
|
||||
rpipe = reader;
|
||||
wpipe = writer;
|
||||
@ -38,7 +44,11 @@ Result<usize> Pipe::read(u8* buf, usize, usize length)
|
||||
|
||||
Result<usize> Pipe::write(const u8* buf, usize, usize length)
|
||||
{
|
||||
if (!m_reader) return length;
|
||||
if (!m_reader)
|
||||
{
|
||||
Scheduler::current()->send_signal(SIGPIPE);
|
||||
return err(EPIPE);
|
||||
}
|
||||
|
||||
u8* slice = TRY(m_data_buffer.slice_at_end(length));
|
||||
memcpy(slice, buf, length);
|
||||
@ -46,7 +56,7 @@ Result<usize> Pipe::write(const u8* buf, usize, usize length)
|
||||
return length;
|
||||
}
|
||||
|
||||
bool Pipe::blocking() const
|
||||
bool Pipe::will_block_if_read() const
|
||||
{
|
||||
return !m_data_buffer.size() && m_writer;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ class PipeInodeBase;
|
||||
class PipeReader;
|
||||
class PipeWriter;
|
||||
|
||||
class Pipe
|
||||
class Pipe : public Shareable
|
||||
{
|
||||
public:
|
||||
static Result<void> create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe);
|
||||
@ -15,7 +15,7 @@ class Pipe
|
||||
|
||||
Result<usize> write(const u8* buf, usize, usize length);
|
||||
|
||||
bool blocking() const;
|
||||
bool will_block_if_read() const;
|
||||
|
||||
private:
|
||||
Buffer m_data_buffer;
|
||||
@ -40,14 +40,15 @@ class PipeInodeBase : public VFS::FileInode
|
||||
return err(ENOTSUP);
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return m_pipe->blocking();
|
||||
return m_pipe->will_block_if_read();
|
||||
}
|
||||
|
||||
usize size() const override
|
||||
const VFS::InodeMetadata& metadata() const override
|
||||
{
|
||||
return m_pipe->m_data_buffer.size();
|
||||
m_metadata.size = m_pipe->m_data_buffer.size();
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
void did_link() override
|
||||
@ -58,23 +59,6 @@ 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;
|
||||
@ -86,8 +70,6 @@ class PipeInodeBase : public VFS::FileInode
|
||||
|
||||
protected:
|
||||
SharedPtr<Pipe> m_pipe;
|
||||
u32 m_uid { 0 };
|
||||
u32 m_gid { 0 };
|
||||
};
|
||||
|
||||
class PipeWriter : public PipeInodeBase
|
||||
@ -104,11 +86,6 @@ class PipeWriter : public PipeInodeBase
|
||||
return m_pipe->write(buf, offset, length);
|
||||
}
|
||||
|
||||
mode_t mode() const override
|
||||
{
|
||||
return 0200;
|
||||
}
|
||||
|
||||
~PipeWriter();
|
||||
|
||||
friend class Pipe;
|
||||
@ -128,11 +105,6 @@ class PipeReader : public PipeInodeBase
|
||||
check(false);
|
||||
}
|
||||
|
||||
mode_t mode() const override
|
||||
{
|
||||
return 0400;
|
||||
}
|
||||
|
||||
~PipeReader();
|
||||
|
||||
friend class Pipe;
|
||||
|
49
kernel/src/fs/StorageCache.cpp
Normal file
49
kernel/src/fs/StorageCache.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#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(); }
|
||||
}
|
37
kernel/src/fs/StorageCache.h
Normal file
37
kernel/src/fs/StorageCache.h
Normal file
@ -0,0 +1,37 @@
|
||||
#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;
|
||||
};
|
@ -69,7 +69,8 @@ namespace VFS
|
||||
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||
SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
@ -81,10 +82,11 @@ namespace VFS
|
||||
|
||||
TRY(validate_filename(child_name.view()));
|
||||
|
||||
return parent_inode->create_subdirectory(child_name.chars());
|
||||
return parent_inode->create_subdirectory(child_name.chars(), mode);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||
SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
@ -96,7 +98,7 @@ namespace VFS
|
||||
|
||||
TRY(validate_filename(child_name.view()));
|
||||
|
||||
return parent_inode->create_file(child_name.chars());
|
||||
return parent_inode->create_file(child_name.chars(), mode);
|
||||
}
|
||||
|
||||
Result<void> validate_filename(StringView name)
|
||||
@ -135,45 +137,51 @@ namespace VFS
|
||||
{
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
|
||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
return inode->mode() & S_IXOTH;
|
||||
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;
|
||||
}
|
||||
|
||||
bool can_write(SharedPtr<Inode> inode, Credentials auth)
|
||||
{
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
|
||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
return inode->mode() & S_IWOTH;
|
||||
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;
|
||||
}
|
||||
|
||||
bool can_read(SharedPtr<Inode> inode, Credentials auth)
|
||||
{
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
|
||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
return inode->mode() & S_IROTH;
|
||||
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;
|
||||
}
|
||||
|
||||
bool is_setuid(SharedPtr<Inode> inode)
|
||||
{
|
||||
return inode->mode() & S_ISUID;
|
||||
return inode->metadata().mode & S_ISUID;
|
||||
}
|
||||
|
||||
bool is_setgid(SharedPtr<Inode> inode)
|
||||
{
|
||||
return inode->mode() & S_ISGID;
|
||||
return inode->metadata().mode & S_ISGID;
|
||||
}
|
||||
|
||||
bool is_sticky(SharedPtr<Inode> inode)
|
||||
{
|
||||
return inode->mode() & S_ISVTX;
|
||||
return inode->metadata().mode & S_ISVTX;
|
||||
}
|
||||
|
||||
bool is_seekable(SharedPtr<Inode> inode)
|
||||
|
@ -1,5 +1,7 @@
|
||||
#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>
|
||||
@ -17,23 +19,44 @@ 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
|
||||
class FileSystem : public Shareable
|
||||
{
|
||||
public:
|
||||
virtual SharedPtr<Inode> root_inode() const = 0;
|
||||
|
||||
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
|
||||
virtual Result<SharedPtr<Inode>> create_file_inode(mode_t mode) = 0;
|
||||
|
||||
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
|
||||
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent, mode_t mode) = 0;
|
||||
|
||||
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
|
||||
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor, mode_t mode) = 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;
|
||||
@ -73,7 +96,7 @@ namespace VFS
|
||||
StaticString<128> name;
|
||||
};
|
||||
|
||||
class Inode
|
||||
class Inode : public Shareable
|
||||
{
|
||||
public:
|
||||
virtual Result<u64> ioctl(int, void*)
|
||||
@ -86,14 +109,23 @@ 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) = 0;
|
||||
virtual Result<SharedPtr<Inode>> create_file(const char* name, mode_t mode) = 0;
|
||||
|
||||
virtual Result<SharedPtr<Inode>> create_subdirectory(const char* name) = 0;
|
||||
virtual Result<SharedPtr<Inode>> create_subdirectory(const char* name, mode_t mode) = 0;
|
||||
|
||||
virtual Result<void> add_entry(SharedPtr<Inode> inode, const char* name) = 0;
|
||||
|
||||
@ -110,7 +142,7 @@ namespace VFS
|
||||
|
||||
virtual Result<void> truncate(usize size) = 0;
|
||||
|
||||
virtual bool blocking() const = 0;
|
||||
virtual bool will_block_if_read() const = 0;
|
||||
|
||||
// Symlink-specific methods
|
||||
virtual Result<StringView> readlink()
|
||||
@ -118,29 +150,16 @@ namespace VFS
|
||||
return StringView {};
|
||||
}
|
||||
|
||||
virtual dev_t device_id() const
|
||||
virtual const InodeMetadata& metadata() const
|
||||
{
|
||||
return luna_dev_makedev(0, 0);
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
// Metadata accessors
|
||||
virtual usize size() const = 0;
|
||||
|
||||
virtual mode_t mode() const = 0;
|
||||
|
||||
virtual nlink_t nlinks() const
|
||||
virtual Result<void> set_metadata(const InodeMetadata& metadata)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual u32 uid() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual u32 gid() const
|
||||
{
|
||||
return 0;
|
||||
m_metadata = metadata;
|
||||
m_metadata.ctime = *Timer::realtime_clock();
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool is_mountpoint() const
|
||||
@ -151,11 +170,6 @@ 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;
|
||||
|
||||
@ -163,8 +177,6 @@ namespace VFS
|
||||
|
||||
virtual InodeType type() const = 0;
|
||||
|
||||
virtual usize inode_number() const = 0;
|
||||
|
||||
virtual void add_handle()
|
||||
{
|
||||
auto* f = fs();
|
||||
@ -176,9 +188,13 @@ namespace VFS
|
||||
auto* f = fs();
|
||||
if (f) f->remove_handle();
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable InodeMetadata m_metadata;
|
||||
Option<u64> m_shmid {};
|
||||
};
|
||||
|
||||
class FileInode : Inode
|
||||
class FileInode : public Inode
|
||||
{
|
||||
public:
|
||||
Result<SharedPtr<Inode>> find(const char*) const override
|
||||
@ -191,12 +207,12 @@ namespace VFS
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char*) override
|
||||
Result<SharedPtr<Inode>> create_file(const char*, mode_t) override
|
||||
{
|
||||
return err(ENOTDIR);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_subdirectory(const char*) override
|
||||
Result<SharedPtr<Inode>> create_subdirectory(const char*, mode_t) override
|
||||
{
|
||||
return err(ENOTDIR);
|
||||
}
|
||||
@ -221,7 +237,7 @@ namespace VFS
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -234,7 +250,7 @@ namespace VFS
|
||||
virtual ~FileInode() = default;
|
||||
};
|
||||
|
||||
class DeviceInode : Inode
|
||||
class DeviceInode : public Inode
|
||||
{
|
||||
public:
|
||||
Result<SharedPtr<Inode>> find(const char*) const override
|
||||
@ -247,12 +263,12 @@ namespace VFS
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char*) override
|
||||
Result<SharedPtr<Inode>> create_file(const char*, mode_t) override
|
||||
{
|
||||
return err(ENOTDIR);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_subdirectory(const char*) override
|
||||
Result<SharedPtr<Inode>> create_subdirectory(const char*, mode_t) override
|
||||
{
|
||||
return err(ENOTDIR);
|
||||
}
|
||||
@ -289,10 +305,10 @@ namespace VFS
|
||||
SharedPtr<VFS::Inode> working_directory = {},
|
||||
bool follow_last_symlink = true);
|
||||
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||
SharedPtr<VFS::Inode> working_directory = {});
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth,
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||
SharedPtr<VFS::Inode> working_directory = {});
|
||||
|
||||
Result<void> validate_filename(StringView name);
|
||||
|
51
kernel/src/fs/devices/BlockDevice.cpp
Normal file
51
kernel/src/fs/devices/BlockDevice.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#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;
|
||||
}
|
40
kernel/src/fs/devices/BlockDevice.h
Normal file
40
kernel/src/fs/devices/BlockDevice.h
Normal file
@ -0,0 +1,40 @@
|
||||
#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;
|
||||
};
|
@ -1,6 +1,8 @@
|
||||
#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"
|
||||
@ -11,6 +13,7 @@
|
||||
#include <luna/Vector.h>
|
||||
|
||||
Vector<SharedPtr<ConsoleDevice>> ConsoleDevice::m_console_devices;
|
||||
bool ConsoleDevice::s_is_in_graphical_mode { false };
|
||||
|
||||
Result<void> ConsoleDevice::create()
|
||||
{
|
||||
@ -31,9 +34,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 ((pid_t)current->pgid == foreground_pgrp) return {};
|
||||
if (current->pgid == foreground_pgrp) return {};
|
||||
|
||||
if ((current->signal_mask & (1 << (signo - 1))) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||
{
|
||||
if (can_succeed) return {};
|
||||
return err(EIO);
|
||||
@ -49,13 +52,7 @@ Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
||||
{
|
||||
TRY(handle_background_process_group(false, SIGTTIN));
|
||||
|
||||
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();
|
||||
length = m_input_buffer.dequeue_data(buf, length);
|
||||
|
||||
if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false;
|
||||
|
||||
@ -66,18 +63,27 @@ 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::blocking() const
|
||||
bool ConsoleDevice::will_block_if_read() const
|
||||
{
|
||||
return m_may_read_without_blocking ? false : m_input_buffer.size() == 0;
|
||||
}
|
||||
|
||||
void ConsoleDevice::did_press_or_release_key(u8 scancode)
|
||||
{
|
||||
for (const auto& device : m_console_devices) { device->process_key_event(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());
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleDevice::process_key_event(u8 scancode)
|
||||
@ -100,6 +106,7 @@ 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;
|
||||
}
|
||||
@ -173,7 +180,7 @@ void ConsoleDevice::process_key_event(u8 scancode)
|
||||
if (m_settings.c_lflag & ECHOCTL)
|
||||
{
|
||||
bool should_echo = true;
|
||||
if (key == '\n' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
|
||||
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
|
||||
|
||||
if (should_echo)
|
||||
{
|
||||
@ -194,6 +201,9 @@ 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: {
|
||||
@ -235,6 +245,13 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class ConsoleDevice : public Device
|
||||
|
||||
static void did_press_or_release_key(u8 scancode);
|
||||
|
||||
bool blocking() const override;
|
||||
bool will_block_if_read() const override;
|
||||
|
||||
Result<u64> ioctl(int request, void* arg) override;
|
||||
|
||||
@ -37,9 +37,10 @@ class ConsoleDevice : public Device
|
||||
mutable Buffer m_input_buffer;
|
||||
Option<pid_t> m_foreground_process_group {};
|
||||
Vector<u8> m_line_buffer;
|
||||
mutable Keyboard::KeyboardState m_kb_state;
|
||||
mutable Keyboard::TTYKeyboardState m_kb_state;
|
||||
|
||||
static Vector<SharedPtr<ConsoleDevice>> m_console_devices;
|
||||
static bool s_is_in_graphical_mode;
|
||||
|
||||
void process_key_event(u8 scancode);
|
||||
|
||||
|
@ -1,14 +1,22 @@
|
||||
#pragma once
|
||||
#include "Log.h"
|
||||
#include <luna/Result.h>
|
||||
#include <luna/SharedPtr.h>
|
||||
#include <luna/StringView.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class Device
|
||||
class Device : public Shareable
|
||||
{
|
||||
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);
|
||||
@ -40,7 +48,10 @@ class Device
|
||||
// Path in devfs.
|
||||
virtual StringView device_path() const = 0;
|
||||
|
||||
virtual bool blocking() const = 0;
|
||||
virtual bool will_block_if_read() const = 0;
|
||||
|
||||
virtual ~Device() = default;
|
||||
|
||||
protected:
|
||||
Option<u64> m_shmid;
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
#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"
|
||||
@ -36,8 +38,7 @@ 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));
|
||||
inode->chmod(descriptor.mode);
|
||||
auto inode = TRY(fs->create_device_inode(descriptor.major, descriptor.minor, descriptor.mode));
|
||||
TRY(fs->root_inode()->add_entry(inode, descriptor.name));
|
||||
|
||||
return {};
|
||||
@ -71,6 +72,8 @@ namespace DeviceRegistry
|
||||
ConsoleDevice::create();
|
||||
FramebufferDevice::create();
|
||||
UARTDevice::create();
|
||||
MouseDevice::create();
|
||||
KeyboardDevice::create();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ namespace DeviceRegistry
|
||||
Disk = 4,
|
||||
DiskPartition = 5,
|
||||
Serial = 6,
|
||||
Input = 7,
|
||||
};
|
||||
|
||||
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
||||
|
@ -1,4 +1,6 @@
|
||||
#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>
|
||||
@ -23,12 +25,43 @@ 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::blocking() const
|
||||
bool FramebufferDevice::will_block_if_read() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ class FramebufferDevice : public Device
|
||||
|
||||
Result<usize> write(const u8*, usize, usize) override;
|
||||
|
||||
bool blocking() const override;
|
||||
Result<u64> query_shared_memory(off_t offset, usize count) override;
|
||||
|
||||
bool will_block_if_read() const override;
|
||||
|
||||
bool is_block_device() const override
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ class FullDevice : public Device
|
||||
return err(ENOSPC);
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
34
kernel/src/fs/devices/KeyboardDevice.cpp
Normal file
34
kernel/src/fs/devices/KeyboardDevice.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#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();
|
||||
}
|
31
kernel/src/fs/devices/KeyboardDevice.h
Normal file
31
kernel/src/fs/devices/KeyboardDevice.h
Normal file
@ -0,0 +1,31 @@
|
||||
#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;
|
||||
};
|
50
kernel/src/fs/devices/MouseDevice.cpp
Normal file
50
kernel/src/fs/devices/MouseDevice.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#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();
|
||||
}
|
31
kernel/src/fs/devices/MouseDevice.h
Normal file
31
kernel/src/fs/devices/MouseDevice.h
Normal file
@ -0,0 +1,31 @@
|
||||
#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;
|
||||
};
|
@ -17,7 +17,7 @@ class NullDevice : public Device
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class UARTDevice : public Device
|
||||
|
||||
Result<usize> write(const u8*, usize, usize) override;
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class ZeroDevice : public Device
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "fs/ext2/FileSystem.h"
|
||||
#include "fs/ext2/Inode.h"
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/Common.h>
|
||||
|
||||
static VFS::InodeType vfs_type_from_ext2_type(mode_t mode)
|
||||
{
|
||||
@ -49,11 +49,21 @@ 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_inum = inum;
|
||||
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 };
|
||||
|
||||
#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->mode(), inode->size());
|
||||
inode->m_raw_inode.mode & 0xf000, inode->metadata().mode, inode->metadata().size);
|
||||
#endif
|
||||
|
||||
m_inode_cache.try_set(inum, inode);
|
||||
@ -107,7 +117,7 @@ namespace Ext2
|
||||
fs->m_host_device = host_device;
|
||||
|
||||
fs->m_block_size = 1024 << fs->m_superblock.log_block_size;
|
||||
fs->m_block_groups = get_blocks_from_size(fs->m_superblock.nr_blocks, fs->m_superblock.blocks_per_block_group);
|
||||
fs->m_block_groups = ceil_div(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 "
|
||||
|
@ -127,17 +127,17 @@ namespace Ext2
|
||||
return m_root_inode;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_file_inode() override
|
||||
Result<SharedPtr<VFS::Inode>> create_file_inode(mode_t) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>) override
|
||||
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>, mode_t) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32) override
|
||||
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32, mode_t) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
@ -7,19 +7,12 @@ 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 > size()) return 0;
|
||||
if (offset + length > size()) length = size() - offset;
|
||||
if (offset > m_metadata.size) return 0;
|
||||
if (offset + length > m_metadata.size) length = m_metadata.size - offset;
|
||||
|
||||
const usize block_size = m_fs->m_block_size;
|
||||
|
||||
@ -95,22 +88,21 @@ namespace Ext2
|
||||
{
|
||||
check(m_type == VFS::InodeType::Directory);
|
||||
|
||||
const usize inode_size = size();
|
||||
const usize inode_size = m_metadata.size;
|
||||
const usize block_size = m_fs->m_block_size;
|
||||
|
||||
u8* const buf = TRY(make_array<u8>(block_size));
|
||||
auto guard = make_scope_guard([buf] { delete[] buf; });
|
||||
auto buf = TRY(Buffer::create_sized(block_size));
|
||||
|
||||
m_entries.clear();
|
||||
|
||||
for (usize offset = 0; offset < inode_size; offset += block_size)
|
||||
{
|
||||
TRY(read(buf, offset, block_size));
|
||||
TRY(read(buf.data(), offset, block_size));
|
||||
|
||||
usize dir_offset = 0;
|
||||
while (dir_offset < block_size)
|
||||
{
|
||||
auto& entry = *(Ext2::RawDirectoryEntry*)&buf[dir_offset];
|
||||
auto& entry = *(Ext2::RawDirectoryEntry*)&buf.data()[dir_offset];
|
||||
|
||||
if (entry.inum != 0)
|
||||
{
|
||||
@ -184,7 +176,7 @@ namespace Ext2
|
||||
|
||||
if (!m_link.is_empty()) return m_link.view();
|
||||
|
||||
const usize length = size();
|
||||
const usize length = m_metadata.size;
|
||||
|
||||
if (length < 60)
|
||||
{
|
||||
|
@ -21,33 +21,6 @@ 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;
|
||||
@ -61,12 +34,12 @@ namespace Ext2
|
||||
{
|
||||
}
|
||||
|
||||
Result<void> chmod(mode_t) override
|
||||
Result<u64> query_shared_memory(off_t, usize) override
|
||||
{
|
||||
return err(EROFS);
|
||||
return err(ENOTSUP);
|
||||
}
|
||||
|
||||
Result<void> chown(u32, u32) override
|
||||
Result<void> set_metadata(const VFS::InodeMetadata&) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
@ -87,14 +60,14 @@ namespace Ext2
|
||||
|
||||
Option<VFS::DirectoryEntry> get(usize) const override;
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_file(const char*) override
|
||||
Result<SharedPtr<VFS::Inode>> create_file(const char*, mode_t) override
|
||||
{
|
||||
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char*) override
|
||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char*, mode_t) override
|
||||
{
|
||||
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
|
||||
@ -122,7 +95,7 @@ namespace Ext2
|
||||
return m_entries.size();
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "fs/tmpfs/FileSystem.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include "fs/tmpfs/Inode.h"
|
||||
#include <luna/Alloc.h>
|
||||
@ -10,17 +11,23 @@ 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({}));
|
||||
root->chmod(0755);
|
||||
SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}, 0755));
|
||||
fs->set_root(root);
|
||||
return (SharedPtr<VFS::FileSystem>)fs;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode()
|
||||
Result<u64> FileSystem::allocate_inode_number()
|
||||
{
|
||||
return m_next_inode_number++;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode(mode_t mode)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -30,10 +37,11 @@ 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)
|
||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent, mode_t mode)
|
||||
{
|
||||
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
|
||||
|
||||
@ -43,11 +51,13 @@ 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)
|
||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor, mode_t mode)
|
||||
{
|
||||
SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));
|
||||
|
||||
@ -58,6 +68,9 @@ 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;
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ namespace TmpFS
|
||||
return m_root_inode;
|
||||
}
|
||||
|
||||
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_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_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;
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "fs/tmpfs/Inode.h"
|
||||
#include "arch/MMU.h"
|
||||
#include "memory/SharedMemory.h"
|
||||
|
||||
namespace TmpFS
|
||||
{
|
||||
@ -43,6 +45,8 @@ namespace TmpFS
|
||||
|
||||
inode->did_link();
|
||||
|
||||
m_metadata.mtime = *Timer::realtime_clock();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -59,21 +63,23 @@ namespace TmpFS
|
||||
|
||||
inode->did_unlink();
|
||||
|
||||
m_metadata.mtime = *Timer::realtime_clock();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> DirInode::create_file(const char* name)
|
||||
Result<SharedPtr<VFS::Inode>> DirInode::create_file(const char* name, mode_t mode)
|
||||
{
|
||||
auto inode = TRY(m_fs->create_file_inode());
|
||||
auto inode = TRY(m_fs->create_file_inode(mode));
|
||||
|
||||
TRY(add_entry(inode, name));
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> DirInode::create_subdirectory(const char* name)
|
||||
Result<SharedPtr<VFS::Inode>> DirInode::create_subdirectory(const char* name, mode_t mode)
|
||||
{
|
||||
auto inode = TRY(m_fs->create_dir_inode(m_self));
|
||||
auto inode = TRY(m_fs->create_dir_inode(m_self, mode));
|
||||
|
||||
TRY(add_entry(inode, name));
|
||||
|
||||
@ -110,6 +116,10 @@ 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;
|
||||
}
|
||||
|
||||
@ -121,11 +131,42 @@ 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 {};
|
||||
}
|
||||
|
||||
usize FileInode::size() const
|
||||
Result<u64> FileInode::query_shared_memory(off_t offset, usize count)
|
||||
{
|
||||
return m_data_buffer.size();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -20,68 +20,29 @@ namespace TmpFS
|
||||
|
||||
void set_inode_number(usize inum, Badge<FileSystem>)
|
||||
{
|
||||
m_inode_number = inum;
|
||||
m_metadata.inum = 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;
|
||||
|
||||
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 {};
|
||||
}
|
||||
Result<u64> query_shared_memory(off_t offset, usize count) override;
|
||||
|
||||
void did_link() override
|
||||
{
|
||||
m_nlinks++;
|
||||
m_metadata.nlinks++;
|
||||
}
|
||||
|
||||
void did_unlink() override
|
||||
{
|
||||
m_nlinks--;
|
||||
m_metadata.nlinks--;
|
||||
}
|
||||
|
||||
virtual ~FileInode() = default;
|
||||
@ -89,11 +50,8 @@ namespace TmpFS
|
||||
private:
|
||||
VFS::FileSystem* m_fs;
|
||||
Buffer m_data_buffer;
|
||||
usize m_inode_number;
|
||||
mode_t m_mode;
|
||||
u32 m_uid { 0 };
|
||||
u32 m_gid { 0 };
|
||||
u32 m_nlinks { 0 };
|
||||
|
||||
friend class FileSystem;
|
||||
};
|
||||
|
||||
class SymlinkInode : public VFS::FileInode
|
||||
@ -113,7 +71,7 @@ namespace TmpFS
|
||||
|
||||
void set_inode_number(usize inum, Badge<FileSystem>)
|
||||
{
|
||||
m_inode_number = inum;
|
||||
m_metadata.inum = inum;
|
||||
}
|
||||
|
||||
Result<void> set_link(StringView link, Badge<FileSystem>)
|
||||
@ -127,11 +85,6 @@ 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);
|
||||
@ -147,51 +100,14 @@ 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_nlinks++;
|
||||
m_metadata.nlinks++;
|
||||
}
|
||||
|
||||
void did_unlink() override
|
||||
{
|
||||
m_nlinks--;
|
||||
m_metadata.nlinks--;
|
||||
}
|
||||
|
||||
Result<StringView> readlink() override
|
||||
@ -204,10 +120,8 @@ namespace TmpFS
|
||||
private:
|
||||
VFS::FileSystem* m_fs;
|
||||
String m_link;
|
||||
usize m_inode_number;
|
||||
u32 m_uid { 0 };
|
||||
u32 m_gid { 0 };
|
||||
u32 m_nlinks { 0 };
|
||||
|
||||
friend class FileSystem;
|
||||
};
|
||||
|
||||
class DeviceInode : public VFS::DeviceInode
|
||||
@ -227,7 +141,7 @@ namespace TmpFS
|
||||
|
||||
void set_inode_number(usize inum, Badge<FileSystem>)
|
||||
{
|
||||
m_inode_number = inum;
|
||||
m_metadata.inum = inum;
|
||||
}
|
||||
|
||||
void set_device(SharedPtr<Device> device, Badge<FileSystem>)
|
||||
@ -237,7 +151,12 @@ namespace TmpFS
|
||||
|
||||
void set_device_id(dev_t id, Badge<FileSystem>)
|
||||
{
|
||||
m_device_id = id;
|
||||
m_metadata.devid = id;
|
||||
}
|
||||
|
||||
Result<u64> query_shared_memory(off_t offset, usize count) override
|
||||
{
|
||||
return m_device->query_shared_memory(offset, count);
|
||||
}
|
||||
|
||||
VFS::FileSystem* fs() const override
|
||||
@ -245,16 +164,6 @@ 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);
|
||||
@ -281,57 +190,19 @@ namespace TmpFS
|
||||
return m_device->isatty();
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() const override
|
||||
{
|
||||
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 {};
|
||||
return m_device->will_block_if_read();
|
||||
}
|
||||
|
||||
void did_link() override
|
||||
{
|
||||
m_nlinks++;
|
||||
m_metadata.nlinks++;
|
||||
}
|
||||
|
||||
void did_unlink() override
|
||||
{
|
||||
m_nlinks--;
|
||||
m_metadata.nlinks--;
|
||||
}
|
||||
|
||||
virtual ~DeviceInode() = default;
|
||||
@ -339,12 +210,8 @@ namespace TmpFS
|
||||
private:
|
||||
VFS::FileSystem* m_fs;
|
||||
SharedPtr<Device> m_device;
|
||||
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 };
|
||||
|
||||
friend class FileSystem;
|
||||
};
|
||||
|
||||
class DirInode : public VFS::Inode
|
||||
@ -359,7 +226,7 @@ namespace TmpFS
|
||||
|
||||
void set_inode_number(usize inum, Badge<FileSystem>)
|
||||
{
|
||||
m_inode_number = inum;
|
||||
m_metadata.inum = inum;
|
||||
}
|
||||
|
||||
void set_self(SharedPtr<VFS::Inode> self, Badge<FileSystem>)
|
||||
@ -385,54 +252,16 @@ namespace TmpFS
|
||||
return err(EISDIR);
|
||||
}
|
||||
|
||||
bool blocking() const override
|
||||
bool will_block_if_read() 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;
|
||||
@ -455,8 +284,8 @@ namespace TmpFS
|
||||
|
||||
Result<void> remove_entry(const char* name) override;
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override;
|
||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(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<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
||||
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
||||
@ -465,13 +294,9 @@ namespace TmpFS
|
||||
|
||||
private:
|
||||
VFS::FileSystem* m_fs;
|
||||
usize m_inode_number;
|
||||
mode_t m_mode;
|
||||
u32 m_uid { 0 };
|
||||
u32 m_gid { 0 };
|
||||
|
||||
SharedPtr<VFS::Inode> m_self;
|
||||
|
||||
Vector<VFS::DirectoryEntry> m_entries;
|
||||
|
||||
friend class FileSystem;
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#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"
|
||||
@ -30,38 +31,56 @@ 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, built on %s at %s", MOON_VERSION, __DATE__, __TIME__);
|
||||
{
|
||||
kinfoln("Starting Moon %s %s", MOON_VERSION, MOON_RELEASE);
|
||||
|
||||
// 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");
|
||||
|
||||
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");
|
||||
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
||||
|
||||
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 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);
|
||||
|
||||
#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();
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
#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>
|
||||
|
||||
@ -49,14 +51,25 @@ 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);
|
||||
}
|
||||
|
||||
ptr->m_directory = TRY(MMU::clone_userspace_page_directory(m_directory));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return move(ptr);
|
||||
}
|
||||
@ -81,8 +94,17 @@ AddressSpace& AddressSpace::operator=(AddressSpace&& other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result<u64> AddressSpace::alloc_region(usize count, bool persistent)
|
||||
Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t offset, u64 shmid, 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)
|
||||
@ -90,8 +112,7 @@ Result<u64> AddressSpace::alloc_region(usize count, bool persistent)
|
||||
if (region->count < count) continue;
|
||||
if (region->count == count)
|
||||
{
|
||||
region->used = true;
|
||||
region->persistent = persistent;
|
||||
update_region(region);
|
||||
u64 address = region->start;
|
||||
try_merge_region_with_neighbors(region);
|
||||
return address;
|
||||
@ -100,8 +121,7 @@ Result<u64> AddressSpace::alloc_region(usize count, bool persistent)
|
||||
u64 boundary = region->end - (count * ARCH_PAGE_SIZE);
|
||||
|
||||
auto* new_region = TRY(split_region(region, boundary));
|
||||
new_region->used = true;
|
||||
new_region->persistent = persistent;
|
||||
update_region(new_region);
|
||||
try_merge_region_with_neighbors(new_region);
|
||||
|
||||
return boundary;
|
||||
@ -111,12 +131,23 @@ Result<u64> AddressSpace::alloc_region(usize count, bool persistent)
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool persistent)
|
||||
Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int prot, int flags, off_t offset, u64 shmid,
|
||||
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;
|
||||
@ -131,13 +162,13 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool
|
||||
|
||||
if (region->start >= address && region->end <= end)
|
||||
{
|
||||
region->used = used;
|
||||
region->persistent = persistent;
|
||||
update_region(region);
|
||||
if (region->start == address && region->end == end)
|
||||
{
|
||||
try_merge_region_with_neighbors(region);
|
||||
return true;
|
||||
}
|
||||
try_merge_region_with_neighbors(region);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -145,8 +176,7 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool
|
||||
{
|
||||
auto* middle_region = TRY(split_region(region, address));
|
||||
TRY(split_region(middle_region, end));
|
||||
middle_region->used = used;
|
||||
middle_region->persistent = persistent;
|
||||
update_region(middle_region);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -154,8 +184,7 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool
|
||||
{
|
||||
bool finished = region->end == end;
|
||||
auto* split = TRY(split_region(region, address));
|
||||
split->used = used;
|
||||
split->persistent = persistent;
|
||||
update_region(split);
|
||||
try_merge_region_with_neighbors(split);
|
||||
if (!finished) continue;
|
||||
return true;
|
||||
@ -164,8 +193,7 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool
|
||||
if (region->end > end)
|
||||
{
|
||||
TRY(split_region(region, end));
|
||||
region->used = used;
|
||||
region->persistent = persistent;
|
||||
update_region(region);
|
||||
try_merge_region_with_neighbors(region);
|
||||
return true;
|
||||
}
|
||||
@ -174,6 +202,23 @@ Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, bool
|
||||
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;
|
||||
@ -184,18 +229,24 @@ 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() && (*prev)->used == region->used && (*prev)->persistent == region->persistent)
|
||||
if (prev.has_value() && equals(*prev, region))
|
||||
{
|
||||
merge_contiguous_regions(*prev, region);
|
||||
region = *prev;
|
||||
}
|
||||
|
||||
auto next = m_regions.next(region);
|
||||
if (next.has_value() && (*next)->used == region->used && (*next)->persistent == region->persistent)
|
||||
{
|
||||
merge_contiguous_regions(region, *next);
|
||||
}
|
||||
if (next.has_value() && equals(*next, region)) { merge_contiguous_regions(region, *next); }
|
||||
}
|
||||
|
||||
Result<VMRegion*> AddressSpace::split_region(VMRegion* parent, u64 boundary)
|
||||
@ -207,6 +258,9 @@ 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;
|
||||
@ -217,6 +271,43 @@ Result<VMRegion*> AddressSpace::split_region(VMRegion* parent, u64 boundary)
|
||||
|
||||
AddressSpace::~AddressSpace()
|
||||
{
|
||||
m_regions.consume([](VMRegion* region) { delete region; });
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <luna/LinkedList.h>
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <luna/Result.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class VMRegion : LinkedListNode<VMRegion>
|
||||
{
|
||||
@ -12,6 +13,13 @@ 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
|
||||
@ -22,18 +30,21 @@ class AddressSpace
|
||||
|
||||
AddressSpace& operator=(AddressSpace&& other);
|
||||
|
||||
Result<u64> alloc_region(usize count, bool persistent = false);
|
||||
Result<u64> alloc_region(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)
|
||||
Result<bool> test_and_alloc_region(u64 address, usize count, int prot, int flags, off_t offset, u64 shmid = 0,
|
||||
bool persistent = false)
|
||||
{
|
||||
return set_region(address, count, true, persistent);
|
||||
return set_region(address, count, true, prot, flags, offset, shmid, persistent);
|
||||
}
|
||||
|
||||
Result<bool> free_region(u64 address, usize count)
|
||||
{
|
||||
return set_region(address, count, false, false);
|
||||
return set_region(address, count, false, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
Result<void> sync_regions(u64 address, usize count);
|
||||
|
||||
static Result<OwnedPtr<AddressSpace>> try_create();
|
||||
|
||||
Result<OwnedPtr<AddressSpace>> clone();
|
||||
@ -44,7 +55,8 @@ class AddressSpace
|
||||
}
|
||||
|
||||
private:
|
||||
Result<bool> set_region(u64 address, usize count, bool used, bool persistent);
|
||||
Result<bool> set_region(u64 address, usize count, bool used, int prot, int flags, off_t offset, u64 shmid,
|
||||
bool persistent);
|
||||
Result<void> create_default_region();
|
||||
Result<void> create_null_region();
|
||||
void try_merge_region_with_neighbors(VMRegion* region);
|
||||
|
@ -1,9 +1,12 @@
|
||||
#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>
|
||||
@ -39,11 +42,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 = get_blocks_from_size(rodata_size, ARCH_PAGE_SIZE);
|
||||
const usize rodata_pages = ceil_div(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 = get_blocks_from_size(data_size, ARCH_PAGE_SIZE);
|
||||
const usize data_pages = ceil_div(data_size, ARCH_PAGE_SIZE);
|
||||
TRY(remap((u64)start_of_kernel_data, data_pages, MMU::NoExecute | MMU::ReadWrite));
|
||||
|
||||
return {};
|
||||
@ -66,7 +69,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 = get_blocks_from_size(physical_address_space_size / ARCH_PAGE_SIZE, 8UL);
|
||||
const usize frame_bitmap_size = ceil_div(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.
|
||||
@ -95,7 +98,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(), get_blocks_from_size(frame_bitmap_size, ARCH_PAGE_SIZE));
|
||||
lock_frames(largest_free_entry.address(), ceil_div(frame_bitmap_size, ARCH_PAGE_SIZE));
|
||||
}
|
||||
|
||||
void init()
|
||||
@ -141,7 +144,12 @@ namespace MemoryManager
|
||||
|
||||
usize index;
|
||||
bool ok = frame_bitmap->find_and_toggle(false, start_index).try_set_value(index);
|
||||
if (!ok) return err(ENOMEM);
|
||||
if (!ok)
|
||||
{
|
||||
kwarnln("OOM alert! Scheduling the OOM thread...");
|
||||
Scheduler::signal_oom_thread();
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
start_index = index + 1;
|
||||
|
||||
@ -226,6 +234,57 @@ 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);
|
||||
@ -358,6 +417,20 @@ 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);
|
||||
@ -442,7 +515,7 @@ namespace MemoryManager
|
||||
{
|
||||
TRY(result.try_append(*(char*)address));
|
||||
address++;
|
||||
if (address % ARCH_PAGE_SIZE)
|
||||
if ((address % ARCH_PAGE_SIZE) == 0)
|
||||
{
|
||||
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
|
||||
}
|
||||
@ -460,7 +533,7 @@ namespace MemoryManager
|
||||
|
||||
uintptr_t diff = address - page;
|
||||
|
||||
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
|
||||
usize pages = ceil_div(size + diff, ARCH_PAGE_SIZE);
|
||||
|
||||
while (pages--)
|
||||
{
|
||||
@ -496,7 +569,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)
|
||||
if ((user_ptr % ARCH_PAGE_SIZE) == 0)
|
||||
{
|
||||
if (!validate_page_access(user_ptr, MMU::ReadWrite | MMU::User)) return false;
|
||||
}
|
||||
@ -524,7 +597,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)
|
||||
if ((user_ptr % ARCH_PAGE_SIZE) == 0)
|
||||
{
|
||||
if (!validate_page_access(user_ptr, MMU::User)) return false;
|
||||
}
|
||||
|
@ -67,6 +67,9 @@ 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);
|
||||
@ -75,6 +78,7 @@ 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);
|
||||
|
||||
|
124
kernel/src/memory/SharedMemory.cpp
Normal file
124
kernel/src/memory/SharedMemory.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#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); }
|
||||
}
|
32
kernel/src/memory/SharedMemory.h
Normal file
32
kernel/src/memory/SharedMemory.h
Normal file
@ -0,0 +1,32 @@
|
||||
#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;
|
72
kernel/src/net/Socket.h
Normal file
72
kernel/src/net/Socket.h
Normal file
@ -0,0 +1,72 @@
|
||||
#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 };
|
||||
};
|
211
kernel/src/net/UnixSocket.cpp
Normal file
211
kernel/src/net/UnixSocket.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
#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;
|
||||
}
|
57
kernel/src/net/UnixSocket.h
Normal file
57
kernel/src/net/UnixSocket.h
Normal file
@ -0,0 +1,57 @@
|
||||
#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);
|
||||
};
|
@ -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];
|
||||
|
||||
|
18
kernel/src/sys/alarm.cpp
Normal file
18
kernel/src/sys/alarm.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#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;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -9,6 +10,8 @@ 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));
|
||||
|
@ -1,6 +1,8 @@
|
||||
#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>
|
||||
|
||||
@ -9,6 +11,10 @@ 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: {
|
||||
|
@ -1,8 +1,9 @@
|
||||
#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>
|
||||
@ -55,13 +56,18 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
{
|
||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
|
||||
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
|
||||
Vector<String> envp;
|
||||
if (args[2]) envp = TRY(copy_string_vector_from_userspace(args[2]));
|
||||
|
||||
String cmdline = TRY(String::join(argv, " "));
|
||||
|
||||
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);
|
||||
@ -70,9 +76,22 @@ 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_elf(inode));
|
||||
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
||||
|
||||
argv = TRY(loader->cmdline(path, move(argv)));
|
||||
|
||||
u64 user_argv = TRY(image->push_string_vector_on_stack(argv));
|
||||
usize user_argc = argv.size();
|
||||
@ -92,17 +111,13 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
{
|
||||
auto& descriptor = current->fd_table[i];
|
||||
if (!descriptor.has_value()) continue;
|
||||
if (descriptor->flags & O_CLOEXEC)
|
||||
{
|
||||
descriptor->inode->remove_handle();
|
||||
descriptor = {};
|
||||
}
|
||||
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
||||
}
|
||||
|
||||
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();
|
||||
if (is_setuid) current->auth.euid = current->auth.suid = inode->metadata().uid;
|
||||
if (is_setgid) current->auth.egid = current->auth.sgid = inode->metadata().gid;
|
||||
|
||||
current->name = path.chars();
|
||||
current->cmdline = cmdline.chars();
|
||||
|
||||
image->apply(current);
|
||||
|
||||
@ -110,6 +125,9 @@ 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, ¤t->regs, sizeof(*regs));
|
||||
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
@ -119,7 +137,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
|
||||
current->has_called_exec = true;
|
||||
|
||||
kinfoln("exec: thread %lu was replaced with %s", current->id, path.chars());
|
||||
kinfoln("exec: thread %d was replaced with %s", current->id, path.chars());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -128,6 +146,8 @@ 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(¤t->regs, regs, sizeof(*regs));
|
||||
@ -141,28 +161,22 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
thread->state = ThreadState::Runnable;
|
||||
thread->is_kernel = false;
|
||||
thread->fp_data.save();
|
||||
thread->name = current->name;
|
||||
thread->cmdline = current->cmdline;
|
||||
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];
|
||||
if (current->fd_table[i].has_value()) current->fd_table[i]->inode->add_handle();
|
||||
}
|
||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
||||
|
||||
image->apply(thread);
|
||||
|
||||
memcpy(&thread->regs, regs, sizeof(*regs));
|
||||
|
||||
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 };
|
||||
}
|
||||
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current->signal_handlers[i];
|
||||
thread->signal_mask = current->signal_mask;
|
||||
|
||||
thread->set_return(0);
|
||||
@ -170,7 +184,7 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
Scheduler::add_thread(thread);
|
||||
|
||||
#ifdef FORK_DEBUG
|
||||
kdbgln("fork: thread %lu forked into child %lu", current->id, thread->id);
|
||||
kdbgln("fork: thread %d forked into child %d", current->id, thread->id);
|
||||
#endif
|
||||
|
||||
return thread->id;
|
||||
|
@ -1,12 +1,15 @@
|
||||
#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>
|
||||
|
||||
@ -22,11 +25,13 @@ 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->blocking())
|
||||
while (descriptor.inode()->will_block_if_read())
|
||||
{
|
||||
if (descriptor.should_block()) kernel_sleep(10);
|
||||
else
|
||||
@ -35,14 +40,18 @@ Result<u64> sys_read(Registers* regs, SyscallArgs args)
|
||||
if (current->interrupted)
|
||||
{
|
||||
kdbgln("signal: read interrupted by signal");
|
||||
if (current->will_invoke_signal_handler()) return err(EINTR);
|
||||
current->process_pending_signals(regs);
|
||||
if (current->will_ignore_pending_signal())
|
||||
{
|
||||
current->process_pending_signals(regs);
|
||||
continue;
|
||||
}
|
||||
return err(EINTR);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -59,15 +68,18 @@ 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->size();
|
||||
if (descriptor.should_append() && VFS::is_seekable(descriptor.inode()))
|
||||
descriptor.offset = descriptor.inode()->metadata().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;
|
||||
}
|
||||
@ -80,11 +92,13 @@ 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;
|
||||
|
||||
@ -92,7 +106,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->size(), offset)); break;
|
||||
case SEEK_END: new_offset = TRY(safe_add((long)descriptor.inode()->metadata().size, offset)); break;
|
||||
default: return err(EINVAL);
|
||||
}
|
||||
|
||||
@ -110,6 +124,8 @@ 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;
|
||||
@ -137,14 +153,14 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
|
||||
descriptor.flags &= ~O_CLOEXEC;
|
||||
return 0;
|
||||
}
|
||||
case F_GETFL: return (u64)(descriptor.flags & ~O_CLOEXEC);
|
||||
case F_GETFL: return (u64)descriptor.status_flags();
|
||||
case F_SETFL: {
|
||||
int arg = (int)args[2];
|
||||
|
||||
descriptor.flags &= ~(O_APPEND | O_NONBLOCK);
|
||||
descriptor.status_flags() &= ~(O_APPEND | O_NONBLOCK);
|
||||
arg &= (O_APPEND | O_NONBLOCK);
|
||||
|
||||
descriptor.flags |= arg;
|
||||
descriptor.status_flags() |= arg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -161,7 +177,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)
|
||||
@ -169,9 +185,10 @@ 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)
|
||||
@ -181,6 +198,8 @@ 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));
|
||||
@ -199,6 +218,8 @@ 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));
|
||||
|
||||
@ -210,8 +231,8 @@ Result<u64> sys_pipe(Registers*, SyscallArgs args)
|
||||
|
||||
TRY(Pipe::create(rpipe, wpipe));
|
||||
|
||||
current->fd_table[rfd] = FileDescriptor { rpipe, 0, O_RDONLY };
|
||||
current->fd_table[wfd] = FileDescriptor { wpipe, 0, O_WRONLY };
|
||||
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 };
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -222,9 +243,98 @@ 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;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
@ -11,22 +12,23 @@ 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->inode_number();
|
||||
kent.inode = entry.inode->metadata().inum;
|
||||
strlcpy(kent.name, entry.name.chars(), entry.name.length() + 1);
|
||||
|
||||
if (!MemoryManager::copy_to_user_typed(ent, &kent)) return err(EFAULT);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user