Compare commits
No commits in common. "fd402083d7d65092059002c44e70e22bb9e54e23" and "e029679fba03224b9dd55f62ba6604622d7e5f4b" have entirely different histories.
fd402083d7
...
e029679fba
8
.gitignore
vendored
@ -2,17 +2,11 @@ Luna.iso
|
|||||||
toolchain/
|
toolchain/
|
||||||
build/
|
build/
|
||||||
initrd/boot/moon
|
initrd/boot/moon
|
||||||
initrd/ksyms
|
|
||||||
env-local.sh
|
env-local.sh
|
||||||
initrd/bin/**
|
initrd/bin/**
|
||||||
base/usr/*
|
base/usr/**
|
||||||
!base/usr/share
|
|
||||||
base/usr/share/*
|
|
||||||
!base/usr/share/fonts
|
|
||||||
!base/usr/share/icons
|
|
||||||
base/etc/skel/LICENSE
|
base/etc/skel/LICENSE
|
||||||
.fakeroot
|
.fakeroot
|
||||||
kernel/config.cmake
|
kernel/config.cmake
|
||||||
ports/out/
|
ports/out/
|
||||||
ports/temp/
|
ports/temp/
|
||||||
ports/dev/
|
|
||||||
|
@ -5,8 +5,8 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
|||||||
|
|
||||||
set(CMAKE_CROSSCOMPILING true)
|
set(CMAKE_CROSSCOMPILING true)
|
||||||
|
|
||||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.6.0)
|
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.5.0)
|
||||||
set(LUNA_RELEASE_NAME "Andromeda")
|
set(LUNA_RELEASE_NAME "Mercury") # Name for alpha releases
|
||||||
|
|
||||||
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||||
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
||||||
@ -45,11 +45,8 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory(libluna)
|
add_subdirectory(libluna)
|
||||||
add_subdirectory(libos)
|
add_subdirectory(libos)
|
||||||
add_subdirectory(libui)
|
|
||||||
add_subdirectory(libc)
|
add_subdirectory(libc)
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
add_subdirectory(apps)
|
add_subdirectory(apps)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
add_subdirectory(shell)
|
add_subdirectory(shell)
|
||||||
add_subdirectory(wind)
|
|
||||||
add_subdirectory(terminal)
|
|
||||||
|
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
BSD 2-Clause License
|
BSD 2-Clause License
|
||||||
|
|
||||||
Copyright (c) 2022-2024, apio.
|
Copyright (c) 2022-2023, apio.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
17
README.md
@ -6,19 +6,20 @@ A simple POSIX-based operating system for personal computers, written in C++. [!
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
- x86_64-compatible lightweight [kernel](kernel/).
|
- x86_64-compatible lightweight [kernel](kernel/).
|
||||||
- Simple round-robin [scheduler](kernel/src/thread/).
|
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/).
|
||||||
- Read-only [ext2](kernel/src/fs/ext2/) filesystem.
|
- [Virtual file system](kernel/src/fs/) with a simple [tmpfs](kernel/src/fs/tmpfs/) and read-only [ext2](kernel/src/fs/ext2/) support.
|
||||||
- Can [load ELF programs](kernel/src/binfmt/ELF.cpp), [shebang scripts](kernel/src/binfmt/Script.cpp) or [arbitrary binary formats](kernel/src/binfmt/BinaryFormat.h) (registered through kernel modules, which are not supported yet =D).
|
- Can [load ELF programs](kernel/src/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).
|
||||||
- [C Library](libc/), aiming for POSIX compatibility, with many features such as local domain sockets, signals, and shared memory.
|
- Boots from an [ext2](apps/preinit.cpp) root filesystem (a bit slow for now).
|
||||||
|
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be almost POSIX-compatible.
|
||||||
- Support for [several third-party programs](ports/), including the [GNU binutils](ports/binutils/PACKAGE) suite of utilities.
|
- 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 to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
||||||
- Everything is 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.
|
- Environment-agnostic [utility library](libluna/), which can be used in both kernel and userspace.
|
||||||
|
- Return-oriented [error propagation](libluna/include/luna/Result.h), inspired by Rust and SerenityOS. No exceptions here :).
|
||||||
- An extensive set of [standard Unix utilities](apps/), from [ls](apps/ls.cpp) to [uname](apps/uname.cpp) to [base64](apps/base64.cpp). Written in modern C++ and very small amounts of code, using Luna's practical [OS library](libos/).
|
- 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/).
|
||||||
- A simple and efficient [windowing system](wind/), providing a lightweight GUI environment (still in development, not many GUI apps exist).
|
|
||||||
|
|
||||||
## Screenshot
|
|
||||||
![Screenshot as of 0.6.0](docs/screenshots/screenshot-0.6.0.png)
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
364
apps/2048.cpp
@ -1,364 +0,0 @@
|
|||||||
#include <luna/String.h>
|
|
||||||
#include <luna/Utf8.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <ui/Alignment.h>
|
|
||||||
#include <ui/App.h>
|
|
||||||
#include <ui/Font.h>
|
|
||||||
#include <ui/Layout.h>
|
|
||||||
|
|
||||||
static ui::Color colors[] = {
|
|
||||||
ui::Color::from_rgb(255, 255, 0), ui::Color::from_rgb(255, 230, 0), ui::Color::from_rgb(255, 210, 0),
|
|
||||||
ui::Color::from_rgb(255, 190, 0), ui::Color::from_rgb(255, 170, 0), ui::Color::from_rgb(255, 150, 0),
|
|
||||||
ui::Color::from_rgb(255, 130, 0), ui::Color::from_rgb(255, 110, 0), ui::Color::from_rgb(255, 90, 0),
|
|
||||||
ui::Color::from_rgb(255, 70, 0), ui::Color::from_rgb(255, 50, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Tile
|
|
||||||
{
|
|
||||||
int number { 0 };
|
|
||||||
int color { 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
class GameWidget final : public ui::Widget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr int MARGIN = 5;
|
|
||||||
|
|
||||||
Result<void> draw(ui::Canvas& canvas) override
|
|
||||||
{
|
|
||||||
int width = m_rect.width / 4;
|
|
||||||
int height = m_rect.height / 4;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
auto subcanvas = canvas.subcanvas(
|
|
||||||
ui::Rect { width * j + MARGIN, height * i + MARGIN, width - MARGIN, height - MARGIN });
|
|
||||||
int index = i * 4 + j;
|
|
||||||
TRY(draw_tile(index, subcanvas));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override
|
|
||||||
{
|
|
||||||
if (!request.pressed) return ui::EventResult::DidNotHandle;
|
|
||||||
|
|
||||||
bool should_add_tile = false;
|
|
||||||
|
|
||||||
switch (request.key)
|
|
||||||
{
|
|
||||||
case 'w': {
|
|
||||||
bool changed;
|
|
||||||
changed = move_up();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
join_up();
|
|
||||||
changed = move_up();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'a': {
|
|
||||||
bool changed;
|
|
||||||
changed = move_left();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
join_left();
|
|
||||||
changed = move_left();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's': {
|
|
||||||
bool changed;
|
|
||||||
changed = move_down();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
join_down();
|
|
||||||
changed = move_down();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd': {
|
|
||||||
bool changed;
|
|
||||||
changed = move_right();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
join_right();
|
|
||||||
changed = move_right();
|
|
||||||
if (changed) should_add_tile = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'r': {
|
|
||||||
reset();
|
|
||||||
return ui::EventResult::DidHandle;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: return ui::EventResult::DidNotHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_add_tile) add_tile();
|
|
||||||
|
|
||||||
return ui::EventResult::DidHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool move_left()
|
|
||||||
{
|
|
||||||
Tile new_tiles[16];
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (tiles[i * 4 + j].number != 0)
|
|
||||||
{
|
|
||||||
new_tiles[i * 4 + pos] = tiles[i * 4 + j];
|
|
||||||
pos += 1;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void join_left()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 3; j++)
|
|
||||||
{
|
|
||||||
auto& from_tile = tiles[i * 4 + j];
|
|
||||||
auto& to_tile = tiles[i * 4 + j + 1];
|
|
||||||
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
||||||
{
|
|
||||||
from_tile.number *= 2;
|
|
||||||
from_tile.color += 1;
|
|
||||||
to_tile.number = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool move_right()
|
|
||||||
{
|
|
||||||
Tile new_tiles[16];
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
int pos = 3;
|
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (tiles[i * 4 + j].number != 0)
|
|
||||||
{
|
|
||||||
new_tiles[i * 4 + pos] = tiles[i * 4 + j];
|
|
||||||
pos -= 1;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void join_right()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 1; j < 4; j++)
|
|
||||||
{
|
|
||||||
auto& from_tile = tiles[i * 4 + j];
|
|
||||||
auto& to_tile = tiles[i * 4 + j - 1];
|
|
||||||
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
||||||
{
|
|
||||||
from_tile.number *= 2;
|
|
||||||
from_tile.color += 1;
|
|
||||||
to_tile.number = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool move_up()
|
|
||||||
{
|
|
||||||
Tile new_tiles[16];
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (tiles[i * 4 + j].number != 0)
|
|
||||||
{
|
|
||||||
new_tiles[pos * 4 + j] = tiles[i * 4 + j];
|
|
||||||
pos += 1;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void join_up()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
auto& from_tile = tiles[i * 4 + j];
|
|
||||||
auto& to_tile = tiles[i * 4 + j + 4];
|
|
||||||
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
||||||
{
|
|
||||||
from_tile.number *= 2;
|
|
||||||
from_tile.color += 1;
|
|
||||||
to_tile.number = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool move_down()
|
|
||||||
{
|
|
||||||
Tile new_tiles[16];
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
int pos = 3;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (tiles[i * 4 + j].number != 0)
|
|
||||||
{
|
|
||||||
new_tiles[pos * 4 + j] = tiles[i * 4 + j];
|
|
||||||
pos -= 1;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(tiles, new_tiles, sizeof(tiles));
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void join_down()
|
|
||||||
{
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
auto& from_tile = tiles[i * 4 + j];
|
|
||||||
auto& to_tile = tiles[i * 4 + j - 4];
|
|
||||||
if (from_tile.number != 0 && from_tile.number == to_tile.number)
|
|
||||||
{
|
|
||||||
from_tile.number *= 2;
|
|
||||||
from_tile.color += 1;
|
|
||||||
to_tile.number = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_tile()
|
|
||||||
{
|
|
||||||
bool can_add_tile = false;
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
if (tiles[i].number == 0)
|
|
||||||
{
|
|
||||||
can_add_tile = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!can_add_tile)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start;
|
|
||||||
do {
|
|
||||||
start = rand() % 16;
|
|
||||||
} while (tiles[start].number != 0);
|
|
||||||
tiles[start].number = 2;
|
|
||||||
tiles[start].color = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
tiles[i].number = 0;
|
|
||||||
tiles[i].color = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_tile();
|
|
||||||
}
|
|
||||||
|
|
||||||
Tile tiles[16];
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result<void> draw_tile(int index, ui::Canvas& canvas)
|
|
||||||
{
|
|
||||||
auto tile = tiles[index];
|
|
||||||
|
|
||||||
if (tile.number == 0)
|
|
||||||
{
|
|
||||||
canvas.fill(ui::GRAY);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.fill(colors[tile.color]);
|
|
||||||
|
|
||||||
auto fmt = TRY(String::format("%d"_sv, tile.number));
|
|
||||||
|
|
||||||
auto font = ui::Font::default_bold_font();
|
|
||||||
auto rect = ui::align({ 0, 0, canvas.width, canvas.height },
|
|
||||||
{ 0, 0, (int)fmt.length() * font->width(), font->height() },
|
|
||||||
ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
|
|
||||||
auto subcanvas = canvas.subcanvas(rect);
|
|
||||||
|
|
||||||
Utf8StringDecoder decoder(fmt.chars());
|
|
||||||
wchar_t buf[4096];
|
|
||||||
TRY(decoder.decode(buf, sizeof(buf)));
|
|
||||||
|
|
||||||
font->render(buf, ui::BLACK, subcanvas);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
srand((unsigned)time(NULL));
|
|
||||||
|
|
||||||
ui::App app;
|
|
||||||
TRY(app.init(argc, argv));
|
|
||||||
|
|
||||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 400 }));
|
|
||||||
app.set_main_window(window);
|
|
||||||
|
|
||||||
window->set_background(ui::BLACK);
|
|
||||||
window->set_title("2048");
|
|
||||||
|
|
||||||
GameWidget game;
|
|
||||||
window->set_main_widget(game);
|
|
||||||
game.reset();
|
|
||||||
|
|
||||||
return app.run();
|
|
||||||
}
|
|
@ -38,14 +38,5 @@ luna_app(sysfuzz.cpp sysfuzz)
|
|||||||
luna_app(cp.cpp cp)
|
luna_app(cp.cpp cp)
|
||||||
luna_app(kill.cpp kill)
|
luna_app(kill.cpp kill)
|
||||||
luna_app(gol.cpp gol)
|
luna_app(gol.cpp gol)
|
||||||
target_link_libraries(gol PUBLIC ui)
|
|
||||||
luna_app(touch.cpp touch)
|
luna_app(touch.cpp touch)
|
||||||
luna_app(free.cpp free)
|
luna_app(free.cpp free)
|
||||||
luna_app(about.cpp about)
|
|
||||||
target_link_libraries(about PUBLIC ui)
|
|
||||||
luna_app(taskbar.cpp taskbar)
|
|
||||||
target_link_libraries(taskbar PUBLIC ui)
|
|
||||||
luna_app(2048.cpp 2048)
|
|
||||||
target_link_libraries(2048 PUBLIC ui)
|
|
||||||
luna_app(clock.cpp clock)
|
|
||||||
target_link_libraries(clock PUBLIC ui)
|
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
#include <luna/String.h>
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
#include <ui/App.h>
|
|
||||||
#include <ui/Button.h>
|
|
||||||
#include <ui/Label.h>
|
|
||||||
#include <ui/Layout.h>
|
|
||||||
|
|
||||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
ui::App app;
|
|
||||||
TRY(app.init(argc, argv));
|
|
||||||
|
|
||||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
|
||||||
app.set_main_window(window);
|
|
||||||
|
|
||||||
window->set_title("About");
|
|
||||||
window->set_background(BACKGROUND_COLOR);
|
|
||||||
|
|
||||||
utsname info;
|
|
||||||
uname(&info);
|
|
||||||
|
|
||||||
ui::VerticalLayout main_layout;
|
|
||||||
window->set_main_widget(main_layout);
|
|
||||||
|
|
||||||
ui::Label title("About Luna");
|
|
||||||
title.set_font(ui::Font::default_bold_font());
|
|
||||||
|
|
||||||
main_layout.add_widget(title);
|
|
||||||
|
|
||||||
ui::VerticalLayout version_info;
|
|
||||||
main_layout.add_widget(version_info);
|
|
||||||
|
|
||||||
ui::Label license("Licensed under the BSD-2-Clause license.");
|
|
||||||
main_layout.add_widget(license);
|
|
||||||
|
|
||||||
String os_release_text = TRY(String::format("OS release: %s"_sv, info.release));
|
|
||||||
ui::Label os_release(os_release_text.view());
|
|
||||||
version_info.add_widget(os_release);
|
|
||||||
|
|
||||||
String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version));
|
|
||||||
ui::Label kernel_version(kernel_version_text.view());
|
|
||||||
version_info.add_widget(kernel_version);
|
|
||||||
|
|
||||||
return app.run();
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
#include <os/Timer.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <ui/App.h>
|
|
||||||
#include <ui/Label.h>
|
|
||||||
|
|
||||||
ui::Label* g_label;
|
|
||||||
|
|
||||||
void update_time()
|
|
||||||
{
|
|
||||||
time_t t = time(NULL);
|
|
||||||
struct tm* tp = localtime(&t);
|
|
||||||
|
|
||||||
static char buf[2048];
|
|
||||||
strftime(buf, sizeof(buf), "%H:%M:%S", tp);
|
|
||||||
|
|
||||||
g_label->set_text(StringView { buf });
|
|
||||||
|
|
||||||
ui::App::the().main_window()->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
ui::App app;
|
|
||||||
TRY(app.init(argc, argv));
|
|
||||||
|
|
||||||
auto* window = TRY(ui::Window::create(ui::Rect { 500, 400, 100, 50 }));
|
|
||||||
app.set_main_window(window);
|
|
||||||
|
|
||||||
window->set_title("Clock");
|
|
||||||
window->set_background(ui::GRAY);
|
|
||||||
|
|
||||||
g_label = TRY(make<ui::Label>("00:00:00"));
|
|
||||||
g_label->set_font(ui::Font::default_bold_font());
|
|
||||||
g_label->set_color(ui::BLACK);
|
|
||||||
|
|
||||||
window->set_main_widget(*g_label);
|
|
||||||
|
|
||||||
update_time();
|
|
||||||
|
|
||||||
auto timer = TRY(os::Timer::create_repeating(1000, update_time));
|
|
||||||
|
|
||||||
return app.run();
|
|
||||||
}
|
|
63
apps/cp.cpp
@ -1,7 +1,6 @@
|
|||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/Directory.h>
|
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
#include <os/Prompt.h>
|
#include <os/Prompt.h>
|
||||||
@ -17,18 +16,10 @@ Result<void> copy_file(StringView source, StringView destination, bool verbose,
|
|||||||
TRY(os::FileSystem::stat(source_file->fd(), st, true));
|
TRY(os::FileSystem::stat(source_file->fd(), st, true));
|
||||||
umask(0);
|
umask(0);
|
||||||
|
|
||||||
if (st.st_mode & S_IFDIR)
|
|
||||||
{
|
|
||||||
os::eprintln("cp: warning: -R not specified, skipping directory %s", source.chars());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
String path;
|
|
||||||
|
|
||||||
if (os::FileSystem::is_directory(destination, true))
|
if (os::FileSystem::is_directory(destination, true))
|
||||||
{
|
{
|
||||||
auto basename = TRY(PathParser::basename(source));
|
auto basename = TRY(PathParser::basename(source));
|
||||||
path = TRY(PathParser::join_paths(destination, basename.view()));
|
auto path = TRY(PathParser::join_paths(destination, basename.view()));
|
||||||
|
|
||||||
if (interactive && os::FileSystem::exists(path.view(), false))
|
if (interactive && os::FileSystem::exists(path.view(), false))
|
||||||
{
|
{
|
||||||
@ -40,18 +31,15 @@ Result<void> copy_file(StringView source, StringView destination, bool verbose,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
path = TRY(String::from_string_view(destination));
|
if (interactive && os::FileSystem::exists(destination, false))
|
||||||
|
|
||||||
if (interactive && os::FileSystem::exists(path.view(), false))
|
|
||||||
{
|
{
|
||||||
auto prompt = TRY(String::format("cp: Overwrite %s with %s?"_sv, path.chars(), source.chars()));
|
auto prompt = TRY(String::format("cp: Overwrite %s with %s?"_sv, destination.chars(), source.chars()));
|
||||||
if (!os::conditional_prompt(prompt.view(), os::DefaultNo)) return {};
|
if (!os::conditional_prompt(prompt.view(), os::DefaultNo)) return {};
|
||||||
}
|
}
|
||||||
|
destination_file = TRY(File::open_or_create(destination, File::ReadWrite, st.st_mode & ~S_IFMT));
|
||||||
destination_file = TRY(File::open_or_create(path.view(), File::ReadWrite, st.st_mode & ~S_IFMT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) os::eprintln("copying %s to %s", source.chars(), path.chars());
|
if (verbose) os::eprintln("copying %s to %s", source.chars(), destination.chars());
|
||||||
|
|
||||||
auto buf = TRY(Buffer::create_sized(4096));
|
auto buf = TRY(Buffer::create_sized(4096));
|
||||||
while (1)
|
while (1)
|
||||||
@ -64,45 +52,12 @@ Result<void> copy_file(StringView source, StringView destination, bool verbose,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> copy_tree(StringView source, StringView destination, bool verbose, bool interactive)
|
|
||||||
{
|
|
||||||
String path;
|
|
||||||
|
|
||||||
if (!os::FileSystem::is_directory(source, true)) return copy_file(source, destination, verbose, interactive);
|
|
||||||
|
|
||||||
if (os::FileSystem::is_directory(destination, true))
|
|
||||||
{
|
|
||||||
auto basename = TRY(PathParser::basename(source));
|
|
||||||
path = TRY(PathParser::join_paths(destination, basename.view()));
|
|
||||||
|
|
||||||
if (!os::FileSystem::exists(path.view(), false)) TRY(os::FileSystem::create_directory(path.view(), 0755));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
path = TRY(String::from_string_view(destination));
|
|
||||||
if (!os::FileSystem::exists(path.view(), false)) TRY(os::FileSystem::create_directory(path.view(), 0755));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dir = TRY(os::Directory::open(source));
|
|
||||||
|
|
||||||
Vector<String> entries = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
|
|
||||||
|
|
||||||
for (const auto& entry : entries)
|
|
||||||
{
|
|
||||||
auto subpath = TRY(PathParser::join_paths(source, entry.view()));
|
|
||||||
TRY(copy_tree(subpath.view(), path.view(), verbose, interactive));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Vector<StringView> files;
|
Vector<StringView> files;
|
||||||
StringView destination;
|
StringView destination;
|
||||||
bool verbose { false };
|
bool verbose { false };
|
||||||
bool interactive { false };
|
bool interactive { false };
|
||||||
bool recursive { false };
|
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Copy files or directories."_sv);
|
parser.add_description("Copy files or directories."_sv);
|
||||||
@ -111,15 +66,9 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
parser.add_positional_argument(destination, "destination"_sv, true);
|
parser.add_positional_argument(destination, "destination"_sv, true);
|
||||||
parser.add_switch_argument(verbose, 'v', "verbose"_sv, "show more information"_sv);
|
parser.add_switch_argument(verbose, 'v', "verbose"_sv, "show more information"_sv);
|
||||||
parser.add_switch_argument(interactive, 'i', "interactive"_sv, "prompt before overwriting existing files"_sv);
|
parser.add_switch_argument(interactive, 'i', "interactive"_sv, "prompt before overwriting existing files"_sv);
|
||||||
parser.add_switch_argument(recursive, 'R', "recursive"_sv, "copy directories recursively"_sv);
|
|
||||||
TRY(parser.parse(argc, argv));
|
TRY(parser.parse(argc, argv));
|
||||||
|
|
||||||
for (const auto& file : files)
|
for (const auto& file : files) { TRY(copy_file(file, destination, verbose, interactive)); }
|
||||||
{
|
|
||||||
if (recursive) TRY(copy_tree(file, destination, verbose, interactive));
|
|
||||||
else
|
|
||||||
TRY(copy_file(file, destination, verbose, interactive));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
116
apps/gol.cpp
@ -1,13 +1,13 @@
|
|||||||
|
#include <alloca.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/Timer.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ui/App.h>
|
|
||||||
#include <ui/Window.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct Cell
|
struct Cell
|
||||||
@ -16,12 +16,16 @@ struct Cell
|
|||||||
bool new_state;
|
bool new_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int g_num_rows = 40;
|
static int g_num_rows = 76;
|
||||||
static int g_num_columns = 60;
|
static int g_num_columns = 102;
|
||||||
|
|
||||||
|
static int g_fb_width;
|
||||||
|
static int g_fb_height;
|
||||||
|
|
||||||
|
static int g_fd;
|
||||||
|
|
||||||
static Cell* g_cells;
|
static Cell* g_cells;
|
||||||
|
static char* g_fb;
|
||||||
static ui::Window* g_window;
|
|
||||||
|
|
||||||
static Result<void> fill_cells()
|
static Result<void> fill_cells()
|
||||||
{
|
{
|
||||||
@ -43,30 +47,31 @@ static Cell& find_cell(int row, int column)
|
|||||||
return g_cells[row * g_num_columns + column];
|
return g_cells[row * g_num_columns + column];
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int BYTES_PER_PIXEL = sizeof(u32);
|
static constexpr int BYTES_PER_PIXEL = 4;
|
||||||
static constexpr ui::Color activated_cell_color = ui::CYAN;
|
|
||||||
static constexpr ui::Color deactivated_cell_color = ui::Color::from_rgb(40, 40, 40);
|
|
||||||
|
|
||||||
static void draw_cells()
|
static void draw_cells()
|
||||||
{
|
{
|
||||||
const int CELL_WIDTH = g_window->canvas().width / g_num_columns;
|
const int CELL_WIDTH = g_fb_width / g_num_columns;
|
||||||
const int CELL_HEIGHT = g_window->canvas().height / g_num_rows;
|
const int CELL_HEIGHT = g_fb_height / g_num_rows;
|
||||||
|
|
||||||
auto canvas = g_window->canvas();
|
|
||||||
|
|
||||||
for (int i = 0; i < g_num_rows; i++)
|
for (int i = 0; i < g_num_rows; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
for (int j = 0; j < g_num_columns; j++)
|
for (int j = 0; j < g_num_columns; j++)
|
||||||
{
|
{
|
||||||
auto subcanvas = canvas.subcanvas(ui::Rect { j * CELL_WIDTH, i * CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT });
|
char* buf = g_fb + (i * g_fb_width * CELL_HEIGHT * BYTES_PER_PIXEL);
|
||||||
|
|
||||||
auto& cell = find_cell(i, j);
|
auto& cell = find_cell(i, j);
|
||||||
ui::Color color = cell.state ? activated_cell_color : deactivated_cell_color;
|
u8 color = cell.state ? 0xff : 0x00;
|
||||||
subcanvas.fill(color);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_window->update();
|
msync(g_fb, g_fb_height * g_fb_width * BYTES_PER_PIXEL, MS_SYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_neighbors(int row, int column)
|
static int find_neighbors(int row, int column)
|
||||||
@ -102,26 +107,73 @@ static void next_generation()
|
|||||||
for (isize i = 0; i < (g_num_rows * g_num_columns); i++) g_cells[i].state = g_cells[i].new_state;
|
for (isize i = 0; i < (g_num_rows * g_num_columns); i++) g_cells[i].state = g_cells[i].new_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update()
|
|
||||||
{
|
|
||||||
next_generation();
|
|
||||||
draw_cells();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
ui::App app;
|
u64 delay_between_iterations = 250;
|
||||||
TRY(app.init(argc, argv));
|
u64 delay_at_end = 3000;
|
||||||
|
u64 num_iterations = 100;
|
||||||
|
|
||||||
g_window = TRY(ui::Window::create(ui::Rect { 200, 200, 600, 400 }));
|
StringView columns;
|
||||||
g_window->set_title("Game of Life");
|
StringView rows;
|
||||||
app.set_main_window(g_window);
|
StringView delay;
|
||||||
|
StringView end_delay;
|
||||||
|
StringView iterations;
|
||||||
|
StringView seed;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("A framebuffer-based implementation for Conway's Game of Life.");
|
||||||
|
parser.add_system_program_info("gol"_sv);
|
||||||
|
parser.add_positional_argument(rows, "rows"_sv, "76"_sv);
|
||||||
|
parser.add_positional_argument(columns, "columns"_sv, "102"_sv);
|
||||||
|
parser.add_value_argument(delay, 'd', "delay"_sv, "the delay between generations (in ms)");
|
||||||
|
parser.add_value_argument(end_delay, 'e', "end-delay"_sv,
|
||||||
|
"after finishing, how much to wait before returning to the shell (in ms)");
|
||||||
|
parser.add_value_argument(iterations, 'i', "iterations"_sv, "how many generations to show (default: 100)");
|
||||||
|
parser.add_value_argument(seed, 's', "seed"_sv, "the seed for the random number generator");
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
g_num_columns = (int)TRY(columns.to_uint());
|
||||||
|
g_num_rows = (int)TRY(rows.to_uint());
|
||||||
|
if (!delay.is_empty()) delay_between_iterations = TRY(delay.to_uint());
|
||||||
|
if (!end_delay.is_empty()) delay_at_end = TRY(end_delay.to_uint());
|
||||||
|
if (!iterations.is_empty()) num_iterations = TRY(iterations.to_uint());
|
||||||
|
if (!seed.is_empty()) srand((unsigned)TRY(seed.to_uint()));
|
||||||
|
else
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
|
||||||
|
g_fd = open("/dev/fb0", O_RDWR);
|
||||||
|
if (g_fd < 0)
|
||||||
|
{
|
||||||
|
perror("gol: cannot open framebuffer for writing");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fb_height = ioctl(g_fd, FB_GET_HEIGHT);
|
||||||
|
g_fb_width = ioctl(g_fd, FB_GET_WIDTH);
|
||||||
|
|
||||||
TRY(fill_cells());
|
TRY(fill_cells());
|
||||||
|
|
||||||
update();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
auto timer = TRY(os::Timer::create_repeating(100, update));
|
draw_cells();
|
||||||
|
|
||||||
return app.run();
|
while (num_iterations--)
|
||||||
|
{
|
||||||
|
usleep(delay_between_iterations * 1000);
|
||||||
|
next_generation();
|
||||||
|
draw_cells();
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(delay_at_end * 1000);
|
||||||
|
|
||||||
|
munmap(g_fb, g_fb_height * g_fb_width * BYTES_PER_PIXEL);
|
||||||
|
free(g_cells);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
143
apps/init.cpp
@ -34,7 +34,6 @@ struct Service
|
|||||||
String standard_output;
|
String standard_output;
|
||||||
String standard_error;
|
String standard_error;
|
||||||
String standard_input;
|
String standard_input;
|
||||||
String working_directory;
|
|
||||||
Option<uid_t> user {};
|
Option<uid_t> user {};
|
||||||
Option<gid_t> group {};
|
Option<gid_t> group {};
|
||||||
bool wait { false };
|
bool wait { false };
|
||||||
@ -53,16 +52,6 @@ static void do_log(const char* format, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_errlog(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request a successful exit from the system (for tests)
|
// Request a successful exit from the system (for tests)
|
||||||
void sigterm_handler(int)
|
void sigterm_handler(int)
|
||||||
{
|
{
|
||||||
@ -86,8 +75,6 @@ static Result<void> service_child(const Service& service, SharedPtr<os::File> ou
|
|||||||
if (error) dup2(error->fd(), STDERR_FILENO);
|
if (error) dup2(error->fd(), STDERR_FILENO);
|
||||||
if (input) dup2(input->fd(), STDIN_FILENO);
|
if (input) dup2(input->fd(), STDIN_FILENO);
|
||||||
|
|
||||||
if (!service.working_directory.is_empty()) chdir(service.working_directory.chars());
|
|
||||||
|
|
||||||
if (service.user.has_value())
|
if (service.user.has_value())
|
||||||
{
|
{
|
||||||
setgid(service.group.value());
|
setgid(service.group.value());
|
||||||
@ -134,7 +121,7 @@ static Result<void> try_start_service(Service& service)
|
|||||||
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
do_errlog("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
do_log("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
||||||
rc.error_string());
|
rc.error_string());
|
||||||
}
|
}
|
||||||
fclose(g_init_log);
|
fclose(g_init_log);
|
||||||
@ -163,7 +150,7 @@ static void start_service(Service& service)
|
|||||||
auto rc = try_start_service(service);
|
auto rc = try_start_service(service);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
do_errlog("[init] failed to start service %s due to error: %s\n", service.name.chars(), rc.error_string());
|
do_log("[init] failed to start service %s due to error: %s\n", service.name.chars(), rc.error_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,12 +230,6 @@ static Result<void> load_service(const os::Path& path)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parts[0].view() == "WorkingDirectory")
|
|
||||||
{
|
|
||||||
service.working_directory = move(parts[1]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_is_system && parts[0].view() == "User")
|
if (g_is_system && parts[0].view() == "User")
|
||||||
{
|
{
|
||||||
auto* pw = getpwnam(parts[1].chars());
|
auto* pw = getpwnam(parts[1].chars());
|
||||||
@ -358,47 +339,6 @@ static void mount_shmfs()
|
|||||||
if (chmod("/dev/shm", 01777) < 0) exit(255);
|
if (chmod("/dev/shm", 01777) < 0) exit(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mount_devpts()
|
|
||||||
{
|
|
||||||
if (mkdir("/dev/pts", 0755) < 0) exit(255);
|
|
||||||
|
|
||||||
if (mount("/dev/pts", "devpts", "devpts") < 0) exit(255);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_for_child(int)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
auto rc = os::Process::wait(os::Process::ANY_CHILD, &status);
|
|
||||||
if (rc.has_error())
|
|
||||||
{
|
|
||||||
do_log("[init] waitpid error %s", rc.error_string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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> sysinit(StringView path)
|
Result<int> sysinit(StringView path)
|
||||||
{
|
{
|
||||||
if (getpid() != 1)
|
if (getpid() != 1)
|
||||||
@ -411,15 +351,14 @@ Result<int> sysinit(StringView path)
|
|||||||
|
|
||||||
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
|
// 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.
|
// can print stuff.
|
||||||
stdin = fopen("/dev/null", "r");
|
stdin = fopen("/dev/console", "r");
|
||||||
stdout = fopen("/dev/kmsg", "w");
|
stdout = fopen("/dev/console", "w");
|
||||||
stderr = fopen("/dev/kmsg", "w");
|
stderr = fopen("/dev/console", "w");
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr));
|
||||||
|
|
||||||
mount_tmpfs();
|
mount_tmpfs();
|
||||||
mount_shmfs();
|
mount_shmfs();
|
||||||
mount_devpts();
|
|
||||||
|
|
||||||
umask(022);
|
umask(022);
|
||||||
|
|
||||||
@ -430,15 +369,46 @@ Result<int> sysinit(StringView path)
|
|||||||
|
|
||||||
set_hostname();
|
set_hostname();
|
||||||
|
|
||||||
if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_errlog("[init] failed to register handler for SIGTERM\n");
|
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_errlog("[init] failed to register handler for SIGQUIT\n");
|
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT\n");
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
||||||
|
|
||||||
if (path.is_empty()) path = "/etc/init";
|
if (path.is_empty()) path = "/etc/init";
|
||||||
start_services(path);
|
start_services(path);
|
||||||
|
|
||||||
while (1) { wait_for_child(0); }
|
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> user_init(StringView path)
|
Result<int> user_init(StringView path)
|
||||||
@ -457,7 +427,38 @@ Result<int> user_init(StringView path)
|
|||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath proc exec", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath proc exec", nullptr));
|
||||||
|
|
||||||
while (1) { wait_for_child(0); }
|
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)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
@ -473,8 +474,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
"change the default service path (/etc/init or /etc/user)");
|
"change the default service path (/etc/init or /etc/user)");
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
signal(SIGCHLD, wait_for_child);
|
|
||||||
|
|
||||||
if (user) return user_init(service_path);
|
if (user) return user_init(service_path);
|
||||||
return sysinit(service_path);
|
return sysinit(service_path);
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ static void mount_devfs()
|
|||||||
|
|
||||||
static void open_std_streams()
|
static void open_std_streams()
|
||||||
{
|
{
|
||||||
stdin = fopen("/dev/null", "r");
|
stdin = fopen("/dev/console", "r");
|
||||||
stdout = fopen("/dev/kmsg", "w");
|
stdout = fopen("/dev/console", "w");
|
||||||
stderr = fopen("/dev/kmsg", "w");
|
stderr = fopen("/dev/console", "w");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fail(const char* message)
|
static void fail(const char* message)
|
||||||
|
@ -14,7 +14,6 @@ static const char* file_type(mode_t mode)
|
|||||||
case S_IFBLK: return "block special device";
|
case S_IFBLK: return "block special device";
|
||||||
case S_IFLNK: return "symbolic link";
|
case S_IFLNK: return "symbolic link";
|
||||||
case S_IFIFO: return "pipe";
|
case S_IFIFO: return "pipe";
|
||||||
case S_IFSOCK: return "socket";
|
|
||||||
default: return "unknown file type";
|
default: return "unknown file type";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
apps/su.cpp
@ -1,6 +1,3 @@
|
|||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <grp.h>
|
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -10,11 +7,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static struct termios orig;
|
static struct termios orig;
|
||||||
static int fd = -1;
|
|
||||||
|
|
||||||
void restore_terminal()
|
void restore_terminal()
|
||||||
{
|
{
|
||||||
tcsetattr(fd, TCSANOW, &orig);
|
tcsetattr(STDIN_FILENO, TCSANOW, &orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_handler(int signo)
|
void signal_handler(int signo)
|
||||||
@ -25,28 +21,21 @@ void signal_handler(int signo)
|
|||||||
|
|
||||||
char* getpass()
|
char* getpass()
|
||||||
{
|
{
|
||||||
char ctty[L_ctermid];
|
if (!isatty(STDIN_FILENO))
|
||||||
ctermid(ctty);
|
|
||||||
|
|
||||||
FILE* f = fopen(ctty, "r");
|
|
||||||
if (!f)
|
|
||||||
{
|
{
|
||||||
perror("Failed to open controlling terminal");
|
// FIXME: Just read from /dev/tty (the controlling terminal). Problem: that doesn't exist yet.
|
||||||
|
fprintf(stderr, "error: password must be read from a terminal!");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = fileno(f);
|
tcsetpgrp(STDIN_FILENO, getpgid(0));
|
||||||
|
|
||||||
tcsetpgrp(fd, getpgid(0));
|
|
||||||
|
|
||||||
fputs("Password: ", stdout);
|
fputs("Password: ", stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
if (tcgetattr(fd, &orig) < 0)
|
if (tcgetattr(STDIN_FILENO, &orig) < 0)
|
||||||
{
|
{
|
||||||
perror("tcgetattr");
|
perror("tcgetattr");
|
||||||
fclose(f);
|
|
||||||
fd = -1;
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,23 +51,18 @@ char* getpass()
|
|||||||
|
|
||||||
struct termios tc = orig;
|
struct termios tc = orig;
|
||||||
tc.c_lflag &= ~ECHO;
|
tc.c_lflag &= ~ECHO;
|
||||||
if (tcsetattr(fd, TCSANOW, &tc) < 0)
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &tc) < 0)
|
||||||
{
|
{
|
||||||
perror("tcsetattr");
|
perror("tcsetattr");
|
||||||
fclose(f);
|
|
||||||
fd = -1;
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char buf[BUFSIZ];
|
static char buf[BUFSIZ];
|
||||||
char* rc = fgets(buf, sizeof(buf), f);
|
char* rc = fgets(buf, sizeof(buf), stdin);
|
||||||
|
|
||||||
restore_terminal();
|
restore_terminal();
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
fd = -1;
|
|
||||||
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
perror("fgets");
|
perror("fgets");
|
||||||
@ -91,30 +75,6 @@ char* getpass()
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> set_supplementary_groups(const char* name)
|
|
||||||
{
|
|
||||||
Vector<gid_t> extra_groups;
|
|
||||||
|
|
||||||
setgrent();
|
|
||||||
group* grp;
|
|
||||||
while ((grp = getgrent()))
|
|
||||||
{
|
|
||||||
for (char** user = grp->gr_mem; *user; user++)
|
|
||||||
{
|
|
||||||
if (!strcmp(*user, name))
|
|
||||||
{
|
|
||||||
TRY(extra_groups.try_append(grp->gr_gid));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endgrent();
|
|
||||||
|
|
||||||
if (setgroups(static_cast<int>(extra_groups.size()), extra_groups.data()) < 0) return err(errno);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView name;
|
StringView name;
|
||||||
@ -148,12 +108,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
signal(SIGTTOU, SIG_IGN);
|
signal(SIGTTOU, SIG_IGN);
|
||||||
|
|
||||||
if (!strcmp(entry->pw_passwd, "!"))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: %s's password is disabled!\n", argv[0], entry->pw_name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* pass = getpass();
|
char* pass = getpass();
|
||||||
if (!pass) return 1;
|
if (!pass) return 1;
|
||||||
|
|
||||||
@ -166,8 +120,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
memset(pass, 0, strlen(pass));
|
memset(pass, 0, strlen(pass));
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(set_supplementary_groups(name.chars()));
|
|
||||||
|
|
||||||
setgid(entry->pw_gid);
|
setgid(entry->pw_gid);
|
||||||
setuid(entry->pw_uid);
|
setuid(entry->pw_uid);
|
||||||
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
#include <os/File.h>
|
|
||||||
#include <os/Process.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <ui/App.h>
|
|
||||||
#include <ui/Button.h>
|
|
||||||
#include <ui/Container.h>
|
|
||||||
#include <ui/Image.h>
|
|
||||||
#include <ui/Layout.h>
|
|
||||||
|
|
||||||
static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
|
|
||||||
|
|
||||||
void sigchld_handler(int)
|
|
||||||
{
|
|
||||||
wait(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, Slice<StringView> args, StringView icon)
|
|
||||||
{
|
|
||||||
auto* button = new (std::nothrow) ui::Button({ 0, 0, 50, 50 });
|
|
||||||
if (!button) return err(ENOMEM);
|
|
||||||
layout.add_widget(*button);
|
|
||||||
|
|
||||||
auto* container = new (std::nothrow)
|
|
||||||
ui::Container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
|
|
||||||
if (!container) return err(ENOMEM);
|
|
||||||
button->set_widget(*container);
|
|
||||||
button->set_action([=] { os::Process::spawn(args[0], args, false); });
|
|
||||||
|
|
||||||
auto image = TRY(ui::ImageWidget::load(icon));
|
|
||||||
container->set_widget(*image);
|
|
||||||
|
|
||||||
image.leak();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
ui::App app;
|
|
||||||
TRY(app.init(argc, argv));
|
|
||||||
|
|
||||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
|
||||||
|
|
||||||
ui::Rect screen = app.screen_rect();
|
|
||||||
|
|
||||||
ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 };
|
|
||||||
|
|
||||||
auto window = TRY(ui::Window::create(bar, ui::WindowType::System));
|
|
||||||
app.set_main_window(window);
|
|
||||||
|
|
||||||
window->set_background(TASKBAR_COLOR);
|
|
||||||
|
|
||||||
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
|
||||||
window->set_main_widget(layout);
|
|
||||||
|
|
||||||
StringView terminal_command[] = { "/usr/bin/terminal" };
|
|
||||||
TRY(create_widget_group_for_app(layout, { terminal_command, 1 }, "/usr/share/icons/32x32/app-terminal.tga"));
|
|
||||||
|
|
||||||
StringView about_command[] = { "/usr/bin/about" };
|
|
||||||
TRY(create_widget_group_for_app(layout, { about_command, 1 }, "/usr/share/icons/32x32/app-about.tga"));
|
|
||||||
|
|
||||||
StringView gol_command[] = { "/usr/bin/gol" };
|
|
||||||
TRY(create_widget_group_for_app(layout, { gol_command, 1 }, "/usr/share/icons/32x32/app-gol.tga"));
|
|
||||||
|
|
||||||
StringView clock_command[] = { "/usr/bin/clock" };
|
|
||||||
TRY(create_widget_group_for_app(layout, { clock_command, 1 }, "/usr/share/icons/32x32/app-clock.tga"));
|
|
||||||
|
|
||||||
return app.run();
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
root:!:0:
|
root:!:0:
|
||||||
users:!:1:selene
|
users:!:1:
|
||||||
wind:!:2:selene
|
|
||||||
selene:!:1000:
|
selene:!:1000:
|
||||||
|
4
base/etc/init/01-motd
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Name=motd
|
||||||
|
Description=Show the message of the day to the user.
|
||||||
|
Command=/usr/bin/cat /etc/motd
|
||||||
|
Wait=true
|
@ -1,6 +1,4 @@
|
|||||||
Name=login
|
Name=login
|
||||||
Description=Start the display server.
|
Description=Start the command-line login program.
|
||||||
Command=/usr/bin/wind --user=selene
|
Command=/usr/bin/login
|
||||||
StandardOutput=/dev/uart0
|
|
||||||
StandardError=/dev/uart0
|
|
||||||
Restart=true
|
Restart=true
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
root:toor:0:0:Administrator:/:/usr/bin/sh
|
root:toor:0:0:Administrator:/:/usr/bin/sh
|
||||||
wind:!:2:2:Window Manager:/:/usr/bin/init
|
|
||||||
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
|
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
Name=taskbar
|
|
||||||
Description=Start the taskbar.
|
|
||||||
Command=/usr/bin/taskbar
|
|
||||||
WorkingDirectory=/home/selene
|
|
||||||
Restart=true
|
|
@ -1,4 +0,0 @@
|
|||||||
Name=terminal
|
|
||||||
Description=Start the terminal.
|
|
||||||
WorkingDirectory=/home/selene
|
|
||||||
Command=/usr/bin/terminal
|
|
Before Width: | Height: | Size: 1004 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 16 KiB |
2
initrd/sbin/mount-ext2fs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mkdir /mnt
|
||||||
|
mount -t ext2 /dev/cd0p2 /mnt
|
@ -8,7 +8,6 @@ set(SOURCES
|
|||||||
src/Log.cpp
|
src/Log.cpp
|
||||||
src/Pledge.cpp
|
src/Pledge.cpp
|
||||||
src/cxxabi.cpp
|
src/cxxabi.cpp
|
||||||
src/Symbols.cpp
|
|
||||||
src/video/Framebuffer.cpp
|
src/video/Framebuffer.cpp
|
||||||
src/video/TextConsole.cpp
|
src/video/TextConsole.cpp
|
||||||
src/memory/MemoryManager.cpp
|
src/memory/MemoryManager.cpp
|
||||||
@ -19,13 +18,11 @@ set(SOURCES
|
|||||||
src/memory/SharedMemory.cpp
|
src/memory/SharedMemory.cpp
|
||||||
src/boot/Init.cpp
|
src/boot/Init.cpp
|
||||||
src/arch/Serial.cpp
|
src/arch/Serial.cpp
|
||||||
|
src/arch/Timer.cpp
|
||||||
src/arch/PCI.cpp
|
src/arch/PCI.cpp
|
||||||
src/lib/Mutex.cpp
|
|
||||||
src/thread/Thread.cpp
|
src/thread/Thread.cpp
|
||||||
src/thread/ThreadImage.cpp
|
src/thread/ThreadImage.cpp
|
||||||
src/thread/Scheduler.cpp
|
src/thread/Scheduler.cpp
|
||||||
src/thread/Clock.cpp
|
|
||||||
src/thread/Timer.cpp
|
|
||||||
src/sys/Syscall.cpp
|
src/sys/Syscall.cpp
|
||||||
src/sys/exit.cpp
|
src/sys/exit.cpp
|
||||||
src/sys/clock_gettime.cpp
|
src/sys/clock_gettime.cpp
|
||||||
@ -48,7 +45,7 @@ set(SOURCES
|
|||||||
src/sys/signal.cpp
|
src/sys/signal.cpp
|
||||||
src/sys/socket.cpp
|
src/sys/socket.cpp
|
||||||
src/sys/poll.cpp
|
src/sys/poll.cpp
|
||||||
src/sys/setitimer.cpp
|
src/sys/alarm.cpp
|
||||||
src/sys/pledge.cpp
|
src/sys/pledge.cpp
|
||||||
src/sys/memstat.cpp
|
src/sys/memstat.cpp
|
||||||
src/fs/VFS.cpp
|
src/fs/VFS.cpp
|
||||||
@ -57,13 +54,9 @@ set(SOURCES
|
|||||||
src/fs/MBR.cpp
|
src/fs/MBR.cpp
|
||||||
src/fs/GPT.cpp
|
src/fs/GPT.cpp
|
||||||
src/fs/StorageCache.cpp
|
src/fs/StorageCache.cpp
|
||||||
src/fs/OpenFileDescription.cpp
|
|
||||||
src/fs/FSRegistry.cpp
|
|
||||||
src/net/UnixSocket.cpp
|
src/net/UnixSocket.cpp
|
||||||
src/fs/tmpfs/FileSystem.cpp
|
src/fs/tmpfs/FileSystem.cpp
|
||||||
src/fs/tmpfs/Inode.cpp
|
src/fs/tmpfs/Inode.cpp
|
||||||
src/fs/devpts/FileSystem.cpp
|
|
||||||
src/fs/devpts/Inode.cpp
|
|
||||||
src/fs/ext2/FileSystem.cpp
|
src/fs/ext2/FileSystem.cpp
|
||||||
src/fs/ext2/Inode.cpp
|
src/fs/ext2/Inode.cpp
|
||||||
src/fs/devices/DeviceRegistry.cpp
|
src/fs/devices/DeviceRegistry.cpp
|
||||||
@ -76,10 +69,6 @@ set(SOURCES
|
|||||||
src/fs/devices/UARTDevice.cpp
|
src/fs/devices/UARTDevice.cpp
|
||||||
src/fs/devices/MouseDevice.cpp
|
src/fs/devices/MouseDevice.cpp
|
||||||
src/fs/devices/KeyboardDevice.cpp
|
src/fs/devices/KeyboardDevice.cpp
|
||||||
src/fs/devices/PTYMultiplexer.cpp
|
|
||||||
src/fs/devices/MasterPTY.cpp
|
|
||||||
src/fs/devices/SlavePTY.cpp
|
|
||||||
src/fs/devices/TTYLink.cpp
|
|
||||||
src/fs/InitRD.cpp
|
src/fs/InitRD.cpp
|
||||||
src/binfmt/ELF.cpp
|
src/binfmt/ELF.cpp
|
||||||
src/binfmt/BinaryFormat.cpp
|
src/binfmt/BinaryFormat.cpp
|
||||||
@ -93,7 +82,7 @@ if("${LUNA_ARCH}" MATCHES "x86_64")
|
|||||||
src/arch/x86_64/Serial.cpp
|
src/arch/x86_64/Serial.cpp
|
||||||
src/arch/x86_64/MMU.cpp
|
src/arch/x86_64/MMU.cpp
|
||||||
src/arch/x86_64/CPU.cpp
|
src/arch/x86_64/CPU.cpp
|
||||||
src/arch/x86_64/Clock.cpp
|
src/arch/x86_64/Timer.cpp
|
||||||
src/arch/x86_64/Thread.cpp
|
src/arch/x86_64/Thread.cpp
|
||||||
src/arch/x86_64/PCI.cpp
|
src/arch/x86_64/PCI.cpp
|
||||||
src/arch/x86_64/Keyboard.cpp
|
src/arch/x86_64/Keyboard.cpp
|
||||||
@ -121,7 +110,7 @@ target_link_libraries(moon luna-freestanding)
|
|||||||
target_compile_definitions(moon PRIVATE IN_MOON)
|
target_compile_definitions(moon PRIVATE IN_MOON)
|
||||||
|
|
||||||
target_compile_options(moon PRIVATE ${COMMON_FLAGS})
|
target_compile_options(moon PRIVATE ${COMMON_FLAGS})
|
||||||
target_compile_options(moon PRIVATE -nostdlib -mcmodel=kernel -ffreestanding -fno-threadsafe-statics)
|
target_compile_options(moon PRIVATE -nostdlib -mcmodel=kernel -ffreestanding)
|
||||||
|
|
||||||
if("${LUNA_ARCH}" MATCHES "x86_64")
|
if("${LUNA_ARCH}" MATCHES "x86_64")
|
||||||
target_compile_options(moon PRIVATE -mno-red-zone)
|
target_compile_options(moon PRIVATE -mno-red-zone)
|
||||||
@ -155,6 +144,4 @@ target_include_directories(moon PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen)
|
|||||||
|
|
||||||
target_link_options(moon PRIVATE LINKER:-T ${CMAKE_CURRENT_LIST_DIR}/moon.ld -nostdlib -nodefaultlibs)
|
target_link_options(moon PRIVATE LINKER:-T ${CMAKE_CURRENT_LIST_DIR}/moon.ld -nostdlib -nodefaultlibs)
|
||||||
|
|
||||||
add_custom_command(TARGET moon POST_BUILD COMMAND ${LUNA_ROOT}/tools/generate-symbols.sh)
|
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/moon" DESTINATION ${LUNA_ROOT}/initrd/boot)
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/moon" DESTINATION ${LUNA_ROOT}/initrd/boot)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include "arch/Serial.h"
|
#include "arch/Serial.h"
|
||||||
#include "thread/Clock.h"
|
#include "arch/Timer.h"
|
||||||
#include "video/TextConsole.h"
|
#include "video/TextConsole.h"
|
||||||
#include <luna/Format.h>
|
#include <luna/Format.h>
|
||||||
#include <luna/SourceLocation.h>
|
#include <luna/SourceLocation.h>
|
||||||
@ -35,10 +35,9 @@ static void log_serial(LogLevel level, const char* format, va_list origin)
|
|||||||
"\x1b[0m ",
|
"\x1b[0m ",
|
||||||
ansi_color_codes_per_log_level[(int)level], log_level_letters[(int)level]);
|
ansi_color_codes_per_log_level[(int)level], log_level_letters[(int)level]);
|
||||||
|
|
||||||
struct timespec time;
|
auto* time = Timer::monotonic_clock();
|
||||||
g_monotonic_clock.get_time(time);
|
|
||||||
|
|
||||||
Serial::printf("%4zu.%.3zu ", time.tv_sec, time.tv_nsec / 1'000'000);
|
Serial::printf("%4zu.%.3zu ", time->tv_sec, time->tv_nsec / 1'000'000);
|
||||||
|
|
||||||
// NOTE: We do this manually because of a lack of vprintf() in both Serial and TextConsole.
|
// NOTE: We do this manually because of a lack of vprintf() in both Serial and TextConsole.
|
||||||
cstyle_format(
|
cstyle_format(
|
||||||
@ -151,10 +150,9 @@ static bool g_check_already_failed = false;
|
|||||||
if (!g_check_already_failed)
|
if (!g_check_already_failed)
|
||||||
{ // Avoid endlessly failing when trying to report a failed check.
|
{ // Avoid endlessly failing when trying to report a failed check.
|
||||||
g_check_already_failed = true;
|
g_check_already_failed = true;
|
||||||
kerrorln("-- KERNEL PANIC: Check failed at %s:%d, in %s: %s --", location.file(), location.line(),
|
kerrorln("KERNEL PANIC: Check failed at %s:%d, in %s: %s", location.file(), location.line(),
|
||||||
location.function(), expr);
|
location.function(), expr);
|
||||||
CPU::print_stack_trace();
|
CPU::print_stack_trace();
|
||||||
kerrorln("-- END KERNEL PANIC --");
|
|
||||||
}
|
}
|
||||||
CPU::efficient_halt();
|
CPU::efficient_halt();
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
#include "Symbols.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "arch/MMU.h"
|
|
||||||
#include "boot/bootboot.h"
|
|
||||||
#include "fs/InitRD.h"
|
|
||||||
#include "lib/Mutex.h"
|
|
||||||
#include <luna/Scanf.h>
|
|
||||||
#include <luna/TarStream.h>
|
|
||||||
#include <luna/Vector.h>
|
|
||||||
|
|
||||||
extern const BOOTBOOT bootboot;
|
|
||||||
|
|
||||||
static bool g_symbols_loaded = false;
|
|
||||||
|
|
||||||
struct Symbol
|
|
||||||
{
|
|
||||||
u64 address;
|
|
||||||
char symbol[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<Symbol> g_symbols;
|
|
||||||
static Mutex g_lock;
|
|
||||||
|
|
||||||
extern const u8 kernel_start;
|
|
||||||
extern const u8 kernel_end;
|
|
||||||
|
|
||||||
static int sscanf(const char* str, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
|
|
||||||
int rc = scanf_impl(str, format, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Symbols
|
|
||||||
{
|
|
||||||
Result<void> load()
|
|
||||||
{
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
const u64 virtual_initrd_address = MMU::translate_physical_address(bootboot.initrd_ptr);
|
|
||||||
|
|
||||||
TarStream stream;
|
|
||||||
stream.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
|
|
||||||
|
|
||||||
TarStream::Entry file;
|
|
||||||
while (TRY(stream.read_next_entry(file)))
|
|
||||||
{
|
|
||||||
if (file.name.view() == "ksyms")
|
|
||||||
{
|
|
||||||
char* string = strtok(const_cast<char*>((const char*)file.data()), "\n");
|
|
||||||
if (!string) return {};
|
|
||||||
|
|
||||||
do {
|
|
||||||
Symbol symbol;
|
|
||||||
memset(symbol.symbol, 0, sizeof(symbol.symbol));
|
|
||||||
|
|
||||||
char unused;
|
|
||||||
int nread;
|
|
||||||
|
|
||||||
sscanf(string, "%lx %c %n", &symbol.address, &unused, &nread);
|
|
||||||
strlcpy(symbol.symbol, string + nread, sizeof(symbol));
|
|
||||||
|
|
||||||
TRY(g_symbols.try_append(symbol));
|
|
||||||
} while ((string = strtok(nullptr, "\n")));
|
|
||||||
|
|
||||||
kinfoln("Successfully loaded %zu kernel debug symbols", g_symbols.size());
|
|
||||||
|
|
||||||
g_symbols_loaded = true;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
StringView lookup(u64 address)
|
|
||||||
{
|
|
||||||
if (!g_symbols_loaded) return StringView {};
|
|
||||||
if (address < (u64)&kernel_start) return StringView {};
|
|
||||||
if (address >= (u64)&kernel_end) return StringView {};
|
|
||||||
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (isize i = (isize)g_symbols.size() - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (g_symbols[i].address <= address) { return StringView { g_symbols[i].symbol }; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringView {};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <luna/StringView.h>
|
|
||||||
#include <luna/Types.h>
|
|
||||||
|
|
||||||
namespace Symbols
|
|
||||||
{
|
|
||||||
Result<void> load();
|
|
||||||
StringView lookup(u64 address);
|
|
||||||
}
|
|
@ -8,9 +8,7 @@
|
|||||||
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
|
_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(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
|
||||||
_e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \
|
_e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \
|
||||||
_e(truncate) _e(ftruncate) _e(utimensat) _e(setitimer) _e(pledge) _e(memstat) \
|
_e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat)
|
||||||
_e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend) \
|
|
||||||
_e(timer_create) _e(timer_settime) _e(timer_gettime) _e(timer_delete)
|
|
||||||
|
|
||||||
enum Syscalls
|
enum Syscalls
|
||||||
{
|
{
|
||||||
|
91
kernel/src/arch/Timer.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "arch/Timer.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "arch/Serial.h"
|
||||||
|
#include "boot/bootboot.h"
|
||||||
|
#include <luna/TypeTraits.h>
|
||||||
|
|
||||||
|
static struct timespec s_monotonic_clock = { 0, 0 };
|
||||||
|
static struct timespec s_realtime_clock;
|
||||||
|
|
||||||
|
static inline constexpr bool isleap(u32 year)
|
||||||
|
{
|
||||||
|
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr u32 make_yday(u32 year, u32 month)
|
||||||
|
{
|
||||||
|
constexpr u16 upto[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||||
|
|
||||||
|
u32 yd = upto[month - 1];
|
||||||
|
if (month > 2 && isleap(year)) yd++;
|
||||||
|
return yd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16
|
||||||
|
static constexpr u64 broken_down_to_unix(u64 year, u64 yday, u64 hour, u64 min, u64 sec)
|
||||||
|
{
|
||||||
|
return sec + min * 60 + hour * 3600 + yday * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 -
|
||||||
|
((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The bootloader encodes the date and time in Binary-Coded Decimal (BCD), which represents decimal digits using
|
||||||
|
// hexadecimal digits. For example, BCD 0x22 is 22 in decimal.
|
||||||
|
// https://gitlab.com/bztsrc/bootboot/-/blob/master/bootboot_spec_1st_ed.pdf, page 15.
|
||||||
|
static inline constexpr u32 bcd_number_to_decimal(u32 num)
|
||||||
|
{
|
||||||
|
return ((num >> 4) * 10) + (num & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 bootloader_time_to_unix(const u8 boottime[8])
|
||||||
|
{
|
||||||
|
const u32 year = bcd_number_to_decimal(boottime[0]) * 100 + bcd_number_to_decimal(boottime[1]);
|
||||||
|
const u32 month = bcd_number_to_decimal(boottime[2]);
|
||||||
|
const u32 day = bcd_number_to_decimal(boottime[3]);
|
||||||
|
const u32 hour = bcd_number_to_decimal(boottime[4]);
|
||||||
|
const u32 minute = bcd_number_to_decimal(boottime[5]);
|
||||||
|
const u32 second = bcd_number_to_decimal(boottime[6]);
|
||||||
|
// "The last byte can store 1/100th second precision, but in lack of support on most platforms, it is 0x00".
|
||||||
|
// Therefore, let's not rely on it.
|
||||||
|
kinfoln("Current time: %.2d/%.2d/%d %.2d:%.2d:%.2d UTC", day, month, year, hour, minute, second);
|
||||||
|
return broken_down_to_unix(year - 1900, make_yday(year, month) + (day - 1), hour, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const BOOTBOOT bootboot;
|
||||||
|
|
||||||
|
namespace Timer
|
||||||
|
{
|
||||||
|
static struct timespec s_interval = { .tv_sec = 0, .tv_nsec = ARCH_TIMER_RESOLUTION * 1000 };
|
||||||
|
|
||||||
|
void tick()
|
||||||
|
{
|
||||||
|
timespecadd(&s_monotonic_clock, &s_interval, &s_monotonic_clock);
|
||||||
|
timespecadd(&s_realtime_clock, &s_interval, &s_realtime_clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize ticks_ms()
|
||||||
|
{
|
||||||
|
return (s_monotonic_clock.tv_sec * 1000) + (s_monotonic_clock.tv_nsec / 1'000'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec* monotonic_clock()
|
||||||
|
{
|
||||||
|
return &s_monotonic_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec* realtime_clock()
|
||||||
|
{
|
||||||
|
return &s_realtime_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
s_realtime_clock.tv_sec = bootloader_time_to_unix(bootboot.datetime);
|
||||||
|
s_realtime_clock.tv_nsec = 0;
|
||||||
|
arch_init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_invoke_scheduler()
|
||||||
|
{
|
||||||
|
return (s_realtime_clock.tv_nsec % 1'000'000) == 0;
|
||||||
|
}
|
29
kernel/src/arch/Timer.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <bits/timespec.h>
|
||||||
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
#ifdef ARCH_X86_64
|
||||||
|
#include "arch/x86_64/Timer.h"
|
||||||
|
#else
|
||||||
|
#error "Unknown architecture."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const usize MS_PER_SECOND = 1000;
|
||||||
|
static const usize US_PER_SECOND = MS_PER_SECOND * 1000;
|
||||||
|
static const usize NS_PER_SECOND = US_PER_SECOND * 1000;
|
||||||
|
|
||||||
|
namespace Timer
|
||||||
|
{
|
||||||
|
void tick();
|
||||||
|
|
||||||
|
usize ticks_ms();
|
||||||
|
|
||||||
|
struct timespec* monotonic_clock();
|
||||||
|
|
||||||
|
struct timespec* realtime_clock();
|
||||||
|
|
||||||
|
void arch_init();
|
||||||
|
void init();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_invoke_scheduler();
|
@ -50,22 +50,19 @@ load_tr:
|
|||||||
|
|
||||||
extern switch_task
|
extern switch_task
|
||||||
|
|
||||||
; Create a new artificial stack frame simulating an IRQ, saving all the callee-saved registers and triggering a task switch.
|
|
||||||
; As we know this function was called directly using the SysV ABI, instead of arbitrarily interrupting any function at any point,
|
|
||||||
; we can trash some of the caller-saved registers to save state (in this case RDI and RCX).
|
|
||||||
global kernel_yield
|
global kernel_yield
|
||||||
kernel_yield:
|
kernel_yield:
|
||||||
mov rdi, [rsp] ; return address is now in RDI (caller-saved)
|
mov rdi, [rsp] ; return address is now in RDI
|
||||||
mov rcx, rsp ; save current RSP in RCX (caller-saved)
|
mov rcx, rsp ; save current RSP
|
||||||
add rcx, 8 ; skip over the return address
|
add rcx, 8 ; skip over the return address
|
||||||
|
|
||||||
mov eax, ss
|
mov eax, ss
|
||||||
push rax ; SS
|
push rax ; SS
|
||||||
push rcx ; RSP (saved in RCX)
|
push rcx ; RSP
|
||||||
pushfq ; RFLAGS
|
pushfq ; RFLAGS
|
||||||
mov eax, cs
|
mov eax, cs
|
||||||
push rax ; CS
|
push rax ; CS
|
||||||
push rdi ; RIP (return address previously on stack, saved to RDI)
|
push rdi ; RIP
|
||||||
|
|
||||||
sub rsp, 24
|
sub rsp, 24
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Symbols.h"
|
|
||||||
#include "api/Mouse.h"
|
#include "api/Mouse.h"
|
||||||
#include "arch/Keyboard.h"
|
#include "arch/Keyboard.h"
|
||||||
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/CPU.h"
|
#include "arch/x86_64/CPU.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "fs/devices/KeyboardDevice.h"
|
#include "fs/devices/ConsoleDevice.h"
|
||||||
#include "fs/devices/MouseDevice.h"
|
#include "fs/devices/MouseDevice.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Clock.h"
|
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include "video/TextConsole.h"
|
#include "video/TextConsole.h"
|
||||||
#include <bits/signal.h>
|
#include <bits/signal.h>
|
||||||
@ -75,16 +74,6 @@ void decode_page_fault_error_code(u64 code)
|
|||||||
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
|
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_stack(Thread* current, Registers* regs)
|
|
||||||
{
|
|
||||||
if (regs->rsp < current->stack.bottom() || regs->rsp >= current->stack.top())
|
|
||||||
kerrorln("Abnormal stack (RSP outside the normal range, %.16lx-%.16lx)", current->stack.bottom(),
|
|
||||||
current->stack.top());
|
|
||||||
|
|
||||||
if (regs->rsp >= (current->stack.bottom() - ARCH_PAGE_SIZE) && regs->rsp < current->stack.bottom())
|
|
||||||
kerrorln("Likely stack overflow (CPU exception inside guard page)");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
||||||
{
|
{
|
||||||
if (err) kerrorln("Caught CPU exception: %s", err);
|
if (err) kerrorln("Caught CPU exception: %s", err);
|
||||||
@ -95,18 +84,15 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
|||||||
kerrorln("R12: %.16lx R13: %.16lx R14: %.16lx R15: %.16lx", regs->r12, regs->r13, regs->r14, regs->r15);
|
kerrorln("R12: %.16lx R13: %.16lx R14: %.16lx R15: %.16lx", regs->r12, regs->r13, regs->r14, regs->r15);
|
||||||
kerrorln("RIP: %.16lx CS: %.16lx SS: %.16lx FLAGS: %.16lx", regs->rip, regs->cs, regs->ss, regs->rflags);
|
kerrorln("RIP: %.16lx CS: %.16lx SS: %.16lx FLAGS: %.16lx", regs->rip, regs->cs, regs->ss, regs->rflags);
|
||||||
|
|
||||||
|
CPU::print_stack_trace_at(regs);
|
||||||
|
|
||||||
if (!is_in_kernel(regs))
|
if (!is_in_kernel(regs))
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
Scheduler::current()->send_signal(signo);
|
||||||
check_stack(current, regs);
|
Scheduler::current()->process_pending_signals(regs);
|
||||||
|
|
||||||
current->send_signal(signo);
|
|
||||||
current->process_pending_signals(regs);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU::print_stack_trace_at(regs);
|
|
||||||
|
|
||||||
CPU::efficient_halt();
|
CPU::efficient_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +102,6 @@ void handle_page_fault(Registers* regs)
|
|||||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
kerrorln("Page fault while accessing %lx!", cr2);
|
kerrorln("Page fault while accessing %lx!", cr2);
|
||||||
|
|
||||||
if (cr2 <= ARCH_PAGE_SIZE) kerrorln("Looks like a null pointer dereference!");
|
|
||||||
|
|
||||||
decode_page_fault_error_code(regs->error);
|
decode_page_fault_error_code(regs->error);
|
||||||
|
|
||||||
handle_cpu_exception(SIGSEGV, nullptr, regs);
|
handle_cpu_exception(SIGSEGV, nullptr, regs);
|
||||||
@ -167,30 +151,21 @@ void io_thread()
|
|||||||
u8 scancode;
|
u8 scancode;
|
||||||
moon::MousePacket packet;
|
moon::MousePacket packet;
|
||||||
|
|
||||||
if (scancode_queue.try_pop(scancode))
|
if (scancode_queue.try_pop(scancode)) { ConsoleDevice::did_press_or_release_key(scancode); }
|
||||||
{
|
|
||||||
static Keyboard::KeyboardState state = {};
|
|
||||||
auto code = Keyboard::decode_scancode(scancode, state);
|
|
||||||
if (code.has_value()) KeyboardDevice::add_keyboard_event(code.release_value());
|
|
||||||
}
|
|
||||||
else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); }
|
else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); }
|
||||||
else
|
else
|
||||||
kernel_wait_for_event();
|
kernel_wait_for_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool should_invoke_scheduler()
|
|
||||||
{
|
|
||||||
struct timespec time;
|
|
||||||
g_realtime_clock.get_time(time);
|
|
||||||
return (time.tv_nsec % 1'000'000) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void timer_interrupt(Registers* regs, void*)
|
static void timer_interrupt(Registers* regs, void*)
|
||||||
{
|
{
|
||||||
g_realtime_clock.tick();
|
Timer::tick();
|
||||||
g_monotonic_clock.tick();
|
if (should_invoke_scheduler())
|
||||||
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
{
|
||||||
|
Scheduler::invoke(regs);
|
||||||
|
TextConsole::tick_cursor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_interrupt(Registers*, void*)
|
static void keyboard_interrupt(Registers*, void*)
|
||||||
@ -392,7 +367,7 @@ namespace CPU
|
|||||||
rbp,
|
rbp,
|
||||||
[](u64 instruction, void* arg) {
|
[](u64 instruction, void* arg) {
|
||||||
int* ptr = (int*)arg;
|
int* ptr = (int*)arg;
|
||||||
kinfoln("#%d at %p in %s", *ptr, (void*)instruction, Symbols::lookup(instruction).chars());
|
kinfoln("#%d at %p", *ptr, (void*)instruction);
|
||||||
(*ptr)++;
|
(*ptr)++;
|
||||||
},
|
},
|
||||||
&frame_index);
|
&frame_index);
|
||||||
@ -411,7 +386,7 @@ namespace CPU
|
|||||||
regs,
|
regs,
|
||||||
[](u64 instruction, void* arg) {
|
[](u64 instruction, void* arg) {
|
||||||
int* ptr = (int*)arg;
|
int* ptr = (int*)arg;
|
||||||
kinfoln("#%d at %p in %s", *ptr, (void*)instruction, Symbols::lookup(instruction).chars());
|
kinfoln("#%d at %p", *ptr, (void*)instruction);
|
||||||
(*ptr)++;
|
(*ptr)++;
|
||||||
},
|
},
|
||||||
&frame_index);
|
&frame_index);
|
||||||
|
@ -106,6 +106,9 @@ bool Thread::deliver_signal(int signo, Registers* current_regs)
|
|||||||
|
|
||||||
memcpy(®s, current_regs, sizeof(regs));
|
memcpy(®s, current_regs, sizeof(regs));
|
||||||
|
|
||||||
|
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
|
regs.rsp -= 128; // Skip the red zone
|
||||||
|
|
||||||
fp_data.save();
|
fp_data.save();
|
||||||
@ -162,5 +165,7 @@ void Thread::sigreturn(Registers* current_regs)
|
|||||||
|
|
||||||
fp_data.restore();
|
fp_data.restore();
|
||||||
|
|
||||||
|
kinfoln("sigreturn: restored program state, sp=%p, ip=%p", (void*)regs.rsp, (void*)regs.rip);
|
||||||
|
|
||||||
memcpy(current_regs, ®s, sizeof(regs));
|
memcpy(current_regs, ®s, sizeof(regs));
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,15 @@
|
|||||||
#include "thread/Clock.h"
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
|
|
||||||
// Every timer tick is equivalent to 250 microseconds.
|
|
||||||
// FIXME: Change ARCH_TIMER_RESOLUTION to use nanoseconds.
|
|
||||||
const usize ARCH_TIMER_RESOLUTION = 250;
|
|
||||||
|
|
||||||
#define PIT_CHANNEL_0 0x40
|
#define PIT_CHANNEL_0 0x40
|
||||||
|
|
||||||
const u64 base_frequency = 1193182;
|
const u64 base_frequency = 1193182;
|
||||||
|
|
||||||
void Clock::arch_init()
|
void Timer::arch_init()
|
||||||
{
|
{
|
||||||
constexpr u16 divisor = (u16)(base_frequency / ((1000 / ARCH_TIMER_RESOLUTION) * 1000));
|
constexpr u16 divisor = (u16)(base_frequency / ((1000 / ARCH_TIMER_RESOLUTION) * 1000));
|
||||||
static_assert(divisor >= 100, "ARCH_TIMER_RESOLUTION is too low");
|
static_assert(divisor >= 100, "ARCH_TIMER_RESOLUTION is too low");
|
||||||
IO::outb(PIT_CHANNEL_0, (u8)(divisor & 0xFF));
|
IO::outb(PIT_CHANNEL_0, (u8)(divisor & 0xFF));
|
||||||
IO::outb(0x80, 0); // short delay
|
IO::outb(0x80, 0); // short delay
|
||||||
IO::outb(PIT_CHANNEL_0, (u8)((divisor & 0xFF00) >> 8));
|
IO::outb(PIT_CHANNEL_0, (u8)((divisor & 0xFF00) >> 8));
|
||||||
|
|
||||||
long resolution = ARCH_TIMER_RESOLUTION * 1000;
|
|
||||||
|
|
||||||
g_realtime_clock.set_resolution(resolution);
|
|
||||||
g_monotonic_clock.set_resolution(resolution);
|
|
||||||
}
|
}
|
6
kernel/src/arch/x86_64/Timer.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
// Every timer tick is equivalent to 250 microseconds.
|
||||||
|
// FIXME: Change ARCH_TIMER_RESOLUTION to use nanoseconds.
|
||||||
|
const usize ARCH_TIMER_RESOLUTION = 250;
|
@ -1,12 +1,10 @@
|
|||||||
#include "arch/x86_64/disk/ATA.h"
|
#include "arch/x86_64/disk/ATA.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.h"
|
|
||||||
#include "arch/Serial.h"
|
#include "arch/Serial.h"
|
||||||
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "fs/MBR.h"
|
#include "fs/MBR.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "thread/Clock.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
@ -34,13 +32,6 @@ static usize copy_ata_string(char* out, u16* in, usize size)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 ticks_ms()
|
|
||||||
{
|
|
||||||
struct timespec time;
|
|
||||||
g_monotonic_clock.get_time(time);
|
|
||||||
return time.tv_sec * 1000 + time.tv_nsec / 1'000'000;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ATA
|
namespace ATA
|
||||||
{
|
{
|
||||||
Result<void> Controller::scan()
|
Result<void> Controller::scan()
|
||||||
@ -206,24 +197,24 @@ namespace ATA
|
|||||||
|
|
||||||
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
||||||
{
|
{
|
||||||
u64 begin = ticks_ms();
|
u64 begin = Timer::ticks_ms();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
||||||
if (reg_value & value) return true;
|
if (reg_value & value) return true;
|
||||||
if ((ticks_ms() - begin) >= timeout) return false;
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||||
kernel_sleep(1);
|
kernel_sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Channel::wait_for_reg_clear(Register reg, u8 value, u64 timeout)
|
bool Channel::wait_for_reg_clear(Register reg, u8 value, u64 timeout)
|
||||||
{
|
{
|
||||||
u64 begin = ticks_ms();
|
u64 begin = Timer::ticks_ms();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
||||||
if ((reg_value & value) == 0) return true;
|
if ((reg_value & value) == 0) return true;
|
||||||
if ((ticks_ms() - begin) >= timeout) return false;
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||||
kernel_sleep(1);
|
kernel_sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +296,7 @@ namespace ATA
|
|||||||
|
|
||||||
for (u8 drive = 0; drive < 2; drive++)
|
for (u8 drive = 0; drive < 2; drive++)
|
||||||
{
|
{
|
||||||
ScopedMutexLock lock(m_lock);
|
ScopedKMutexLock<100> lock(m_lock);
|
||||||
|
|
||||||
select(drive);
|
select(drive);
|
||||||
|
|
||||||
@ -512,17 +503,11 @@ namespace ATA
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u8 buf[8];
|
if (m_identify_data.big_lba) m_is_lba48 = true;
|
||||||
memcpy(buf, &m_identify_words[100], 8);
|
|
||||||
|
|
||||||
m_block_count = *reinterpret_cast<u64*>(buf);
|
if (m_is_lba48) m_block_count = m_identify_data.sectors_48;
|
||||||
|
else
|
||||||
if (!m_block_count)
|
m_block_count = m_identify_data.sectors_28;
|
||||||
{
|
|
||||||
memcpy(buf, &m_identify_words[60], 4);
|
|
||||||
m_block_count = *reinterpret_cast<u32*>(buf);
|
|
||||||
}
|
|
||||||
else { m_is_lba48 = true; }
|
|
||||||
|
|
||||||
// FIXME: Should we check for CHS?
|
// FIXME: Should we check for CHS?
|
||||||
|
|
||||||
@ -766,7 +751,7 @@ ATADevice::ATADevice(ATA::Drive* drive) : BlockDevice(drive->block_size(), drive
|
|||||||
|
|
||||||
Result<void> ATADevice::read_block(Buffer& buf, u64 block) const
|
Result<void> ATADevice::read_block(Buffer& buf, u64 block) const
|
||||||
{
|
{
|
||||||
ScopedMutexLock lock(m_drive->channel()->lock());
|
ScopedKMutexLock<100> lock(m_drive->channel()->lock());
|
||||||
|
|
||||||
if (buf.size() != m_drive->block_size())
|
if (buf.size() != m_drive->block_size())
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "arch/PCI.h"
|
#include "arch/PCI.h"
|
||||||
#include "fs/devices/BlockDevice.h"
|
#include "fs/devices/BlockDevice.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "lib/Mutex.h"
|
#include "lib/KMutex.h"
|
||||||
#include <luna/Atomic.h>
|
#include <luna/Atomic.h>
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
#include <luna/StaticString.h>
|
#include <luna/StaticString.h>
|
||||||
@ -244,7 +244,7 @@ namespace ATA
|
|||||||
return m_interrupt_line;
|
return m_interrupt_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex& lock()
|
KMutex<100>& lock()
|
||||||
{
|
{
|
||||||
return m_lock;
|
return m_lock;
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ namespace ATA
|
|||||||
bool m_is_pci_native_mode;
|
bool m_is_pci_native_mode;
|
||||||
u8 m_interrupt_line;
|
u8 m_interrupt_line;
|
||||||
|
|
||||||
Mutex m_lock {};
|
KMutex<100> m_lock {};
|
||||||
|
|
||||||
Thread* m_thread { nullptr };
|
Thread* m_thread { nullptr };
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "binfmt/BinaryFormat.h"
|
#include "binfmt/BinaryFormat.h"
|
||||||
#include "binfmt/ELF.h"
|
#include "binfmt/ELF.h"
|
||||||
#include "binfmt/Script.h"
|
#include "binfmt/Script.h"
|
||||||
#include "lib/Mutex.h"
|
|
||||||
|
|
||||||
struct BinaryFormatDescriptor
|
struct BinaryFormatDescriptor
|
||||||
{
|
{
|
||||||
@ -11,8 +10,6 @@ struct BinaryFormatDescriptor
|
|||||||
|
|
||||||
Vector<BinaryFormatDescriptor> g_binary_formats;
|
Vector<BinaryFormatDescriptor> g_binary_formats;
|
||||||
|
|
||||||
static Mutex g_lock;
|
|
||||||
|
|
||||||
Result<void> BinaryFormat::init()
|
Result<void> BinaryFormat::init()
|
||||||
{
|
{
|
||||||
TRY(register_binary_format(ELFLoader::create, nullptr));
|
TRY(register_binary_format(ELFLoader::create, nullptr));
|
||||||
@ -23,7 +20,6 @@ Result<void> BinaryFormat::init()
|
|||||||
|
|
||||||
Result<void> BinaryFormat::register_binary_format(binfmt_loader_creator_t creator, void* arg)
|
Result<void> BinaryFormat::register_binary_format(binfmt_loader_creator_t creator, void* arg)
|
||||||
{
|
{
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
return g_binary_formats.try_append({ creator, arg });
|
return g_binary_formats.try_append({ creator, arg });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +27,6 @@ Result<SharedPtr<BinaryFormatLoader>> BinaryFormat::create_loader(SharedPtr<VFS:
|
|||||||
{
|
{
|
||||||
if (recursion_level >= 8) return err(ELOOP);
|
if (recursion_level >= 8) return err(ELOOP);
|
||||||
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& format : g_binary_formats)
|
for (const auto& format : g_binary_formats)
|
||||||
{
|
{
|
||||||
auto loader = TRY(format.creator(inode, format.arg, recursion_level));
|
auto loader = TRY(format.creator(inode, format.arg, recursion_level));
|
||||||
|
@ -35,9 +35,9 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
|
|||||||
auto& interpreter_path = m_interpreter_cmdline[0];
|
auto& interpreter_path = m_interpreter_cmdline[0];
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, ¤t->extra_groups,
|
auto interpreter =
|
||||||
current->current_directory, true));
|
TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, current->current_directory, true));
|
||||||
if (!VFS::can_execute(interpreter, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(interpreter, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
||||||
u64 entry = TRY(loader->load(space));
|
u64 entry = TRY(loader->load(space));
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file FSRegistry.cpp
|
|
||||||
* @author apio (cloudapio.eu)
|
|
||||||
* @brief Kernel registry of available file system implementations.
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2023, the Luna authors.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs/FSRegistry.h"
|
|
||||||
#include "fs/devpts/FileSystem.h"
|
|
||||||
#include "fs/ext2/FileSystem.h"
|
|
||||||
#include "fs/tmpfs/FileSystem.h"
|
|
||||||
#include "lib/Mutex.h"
|
|
||||||
|
|
||||||
struct VirtualFileSystemDescriptor
|
|
||||||
{
|
|
||||||
StringView name;
|
|
||||||
VirtualFileSystemFactoryFunction factory_function;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PhysicalFileSystemDescriptor
|
|
||||||
{
|
|
||||||
StringView name;
|
|
||||||
PhysicalFileSystemFactoryFunction factory_function;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<VirtualFileSystemDescriptor> g_virtual_filesystems;
|
|
||||||
Vector<PhysicalFileSystemDescriptor> g_physical_filesystems;
|
|
||||||
|
|
||||||
static Mutex g_lock;
|
|
||||||
|
|
||||||
namespace FSRegistry
|
|
||||||
{
|
|
||||||
Result<void> init()
|
|
||||||
{
|
|
||||||
TRY(register_virtual_filesystem(TmpFS::FileSystem::create, "tmpfs"));
|
|
||||||
TRY(register_virtual_filesystem(DevPTS::FileSystem::create, "devpts"));
|
|
||||||
TRY(register_virtual_filesystem(DeviceRegistry::create_devfs_instance, "devfs"));
|
|
||||||
TRY(register_physical_filesystem(Ext2::FileSystem::create, "ext2"));
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> register_virtual_filesystem(VirtualFileSystemFactoryFunction factory_function, StringView name)
|
|
||||||
{
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_virtual_filesystems)
|
|
||||||
{
|
|
||||||
if (descriptor.name == name) return err(EEXIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto descriptor = VirtualFileSystemDescriptor { .name = name, .factory_function = factory_function };
|
|
||||||
|
|
||||||
return g_virtual_filesystems.try_append(move(descriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> register_physical_filesystem(PhysicalFileSystemFactoryFunction factory_function, StringView name)
|
|
||||||
{
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_physical_filesystems)
|
|
||||||
{
|
|
||||||
if (descriptor.name == name) return err(EEXIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto descriptor = PhysicalFileSystemDescriptor { .name = name, .factory_function = factory_function };
|
|
||||||
|
|
||||||
return g_physical_filesystems.try_append(move(descriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<VirtualFileSystemFactoryFunction> lookup_virtual_filesystem(StringView name)
|
|
||||||
{
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_virtual_filesystems)
|
|
||||||
{
|
|
||||||
if (descriptor.name == name) return descriptor.factory_function;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err(ENODEV);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<PhysicalFileSystemFactoryFunction> lookup_physical_filesystem(StringView name)
|
|
||||||
{
|
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_physical_filesystems)
|
|
||||||
{
|
|
||||||
if (descriptor.name == name) return descriptor.factory_function;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err(ENODEV);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file FSRegistry.h
|
|
||||||
* @author apio (cloudapio.eu)
|
|
||||||
* @brief Kernel registry of available file system implementations.
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2023, the Luna authors.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/Device.h"
|
|
||||||
|
|
||||||
typedef Result<SharedPtr<VFS::FileSystem>> (*VirtualFileSystemFactoryFunction)(void);
|
|
||||||
typedef Result<SharedPtr<VFS::FileSystem>> (*PhysicalFileSystemFactoryFunction)(SharedPtr<Device>);
|
|
||||||
|
|
||||||
namespace FSRegistry
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @brief Register the built-in file system implementations.
|
|
||||||
*
|
|
||||||
* @return Result<void> Whether the operation succeeded.
|
|
||||||
*/
|
|
||||||
Result<void> init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register a "virtual" (in-memory) file system implementation, associating a name to it.
|
|
||||||
*
|
|
||||||
* @param factory_function The factory function to use to create new instances of this file system.
|
|
||||||
* @param name The name to associate with this file system implementation.
|
|
||||||
* @return Result<void> Whether the operation succeeded.
|
|
||||||
*/
|
|
||||||
Result<void> register_virtual_filesystem(VirtualFileSystemFactoryFunction factory_function, StringView name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register a "physical" (mounted from a device) file system implementation, associating a name
|
|
||||||
* to it.
|
|
||||||
*
|
|
||||||
* @param factory_function The factory function to use to create new instances of this file system.
|
|
||||||
* @param name The name to associate with this file system implementation.
|
|
||||||
* @return Result<void> Whether the operation succeeded.
|
|
||||||
*/
|
|
||||||
Result<void> register_physical_filesystem(PhysicalFileSystemFactoryFunction factory_function, StringView name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Find a "virtual" (in-memory) file system implementation by name.
|
|
||||||
*
|
|
||||||
* @param name The name to look for.
|
|
||||||
* @return Result<VirtualFileSystemFactoryFunction> An error, or the factory function to create file systems of this
|
|
||||||
* type.
|
|
||||||
*/
|
|
||||||
Result<VirtualFileSystemFactoryFunction> lookup_virtual_filesystem(StringView name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Find a "physical" (mounted from a device) file system implementation by name.
|
|
||||||
*
|
|
||||||
* @param name The name to look for.
|
|
||||||
* @return Result<PhysicalFileSystemFactoryFunction> An error, or the factory function to create file systems of
|
|
||||||
* this type.
|
|
||||||
*/
|
|
||||||
Result<PhysicalFileSystemFactoryFunction> lookup_physical_filesystem(StringView name);
|
|
||||||
}
|
|
@ -20,7 +20,7 @@ void InitRD::initialize()
|
|||||||
|
|
||||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
||||||
{
|
{
|
||||||
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {}, nullptr);
|
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {});
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
if (rc.error() == EEXIST) return {};
|
if (rc.error() == EEXIST) return {};
|
||||||
@ -37,8 +37,7 @@ Result<void> InitRD::populate_vfs()
|
|||||||
{
|
{
|
||||||
if (entry.type == TarStream::EntryType::RegularFile)
|
if (entry.type == TarStream::EntryType::RegularFile)
|
||||||
{
|
{
|
||||||
auto file =
|
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}));
|
||||||
TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}, nullptr));
|
|
||||||
file->write(entry.data(), 0, entry.size);
|
file->write(entry.data(), 0, entry.size);
|
||||||
}
|
}
|
||||||
else if (entry.type == TarStream::EntryType::Directory)
|
else if (entry.type == TarStream::EntryType::Directory)
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
#include "fs/OpenFileDescription.h"
|
|
||||||
#include <bits/open-flags.h>
|
|
||||||
|
|
||||||
OpenFileDescription::OpenFileDescription(SharedPtr<VFS::Inode> ino, int fl) : inode(ino), flags(fl)
|
|
||||||
{
|
|
||||||
inode->add_handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenFileDescription::~OpenFileDescription()
|
|
||||||
{
|
|
||||||
inode->remove_handle();
|
|
||||||
inode->did_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileDescriptor::should_append()
|
|
||||||
{
|
|
||||||
return description->flags & O_APPEND;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileDescriptor::should_block()
|
|
||||||
{
|
|
||||||
return !(description->flags & O_NONBLOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileDescriptor::is_readable()
|
|
||||||
{
|
|
||||||
return description->flags & O_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileDescriptor::is_writable()
|
|
||||||
{
|
|
||||||
return description->flags & O_WRONLY;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include <luna/SharedPtr.h>
|
|
||||||
#include <luna/String.h>
|
|
||||||
|
|
||||||
struct OpenFileDescription : public Shareable
|
|
||||||
{
|
|
||||||
SharedPtr<VFS::Inode> inode;
|
|
||||||
int flags { 0 };
|
|
||||||
String path;
|
|
||||||
|
|
||||||
OpenFileDescription(SharedPtr<VFS::Inode>, int);
|
|
||||||
~OpenFileDescription();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileDescriptor
|
|
||||||
{
|
|
||||||
SharedPtr<OpenFileDescription> description;
|
|
||||||
usize offset { 0 };
|
|
||||||
int flags { 0 };
|
|
||||||
|
|
||||||
bool should_append();
|
|
||||||
bool should_block();
|
|
||||||
bool is_writable();
|
|
||||||
bool is_readable();
|
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> inode()
|
|
||||||
{
|
|
||||||
return description->inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int& status_flags()
|
|
||||||
{
|
|
||||||
return description->flags;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "lib/Mutex.h"
|
#include "lib/KMutex.h"
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/HashMap.h>
|
#include <luna/HashMap.h>
|
||||||
#include <luna/LinkedList.h>
|
#include <luna/LinkedList.h>
|
||||||
@ -33,5 +33,5 @@ class StorageCache : public LinkedListNode<StorageCache>
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
HashMap<u64, CacheEntry> m_cache_entries;
|
HashMap<u64, CacheEntry> m_cache_entries;
|
||||||
Mutex m_mutex;
|
KMutex<100> m_mutex;
|
||||||
};
|
};
|
||||||
|
@ -17,9 +17,8 @@ namespace VFS
|
|||||||
|
|
||||||
static constexpr int MAX_SYMLINKS = 8;
|
static constexpr int MAX_SYMLINKS = 8;
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, SharedPtr<Inode> current_inode,
|
||||||
SharedPtr<Inode> current_inode, bool follow_last_symlink,
|
bool follow_last_symlink, int& symlinks_followed)
|
||||||
int& symlinks_followed)
|
|
||||||
{
|
{
|
||||||
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ namespace VFS
|
|||||||
const char* section;
|
const char* section;
|
||||||
while (parser.next().try_set_value(section))
|
while (parser.next().try_set_value(section))
|
||||||
{
|
{
|
||||||
if (!can_execute(current_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_execute(current_inode, auth)) return err(EACCES);
|
||||||
current_inode = TRY(current_inode->find(section));
|
current_inode = TRY(current_inode->find(section));
|
||||||
|
|
||||||
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
|
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
|
||||||
@ -46,8 +45,7 @@ namespace VFS
|
|||||||
symlink_root = parent_inode;
|
symlink_root = parent_inode;
|
||||||
|
|
||||||
symlinks_followed++;
|
symlinks_followed++;
|
||||||
current_inode =
|
current_inode = TRY(resolve_path_impl(link.chars(), auth, symlink_root, true, symlinks_followed));
|
||||||
TRY(resolve_path_impl(link.chars(), auth, extra_groups, symlink_root, true, symlinks_followed));
|
|
||||||
symlinks_followed--;
|
symlinks_followed--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +55,8 @@ namespace VFS
|
|||||||
return current_inode;
|
return current_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
|
||||||
SharedPtr<VFS::Inode> working_directory, bool follow_last_symlink)
|
bool follow_last_symlink)
|
||||||
{
|
{
|
||||||
SharedPtr<Inode> current_inode;
|
SharedPtr<Inode> current_inode;
|
||||||
|
|
||||||
@ -68,17 +66,17 @@ namespace VFS
|
|||||||
|
|
||||||
int symlinks_followed = 0;
|
int symlinks_followed = 0;
|
||||||
|
|
||||||
return resolve_path_impl(path, auth, extra_groups, current_inode, follow_last_symlink, symlinks_followed);
|
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -88,13 +86,13 @@ namespace VFS
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -135,8 +133,7 @@ namespace VFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Check all three permissions even if the UID or GID match.
|
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
|
||||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
|
||||||
{
|
{
|
||||||
if (auth.euid == 0) return true;
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
@ -145,19 +142,10 @@ namespace VFS
|
|||||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
|
||||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
|
||||||
|
|
||||||
if (extra_groups)
|
|
||||||
{
|
|
||||||
for (gid_t group : *extra_groups)
|
|
||||||
{
|
|
||||||
if (metadata.gid == group) return metadata.mode & S_IXGRP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata.mode & S_IXOTH;
|
return metadata.mode & S_IXOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Check all three permissions even if the UID or GID match.
|
bool can_write(SharedPtr<Inode> inode, Credentials auth)
|
||||||
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
|
||||||
{
|
{
|
||||||
if (auth.euid == 0) return true;
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
@ -166,19 +154,10 @@ namespace VFS
|
|||||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
|
||||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
|
||||||
|
|
||||||
if (extra_groups)
|
|
||||||
{
|
|
||||||
for (gid_t group : *extra_groups)
|
|
||||||
{
|
|
||||||
if (metadata.gid == group) return metadata.mode & S_IWGRP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata.mode & S_IWOTH;
|
return metadata.mode & S_IWOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Check all three permissions even if the UID or GID match.
|
bool can_read(SharedPtr<Inode> inode, Credentials auth)
|
||||||
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
|
||||||
{
|
{
|
||||||
if (auth.euid == 0) return true;
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
@ -187,14 +166,6 @@ namespace VFS
|
|||||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
|
||||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
|
||||||
|
|
||||||
if (extra_groups)
|
|
||||||
{
|
|
||||||
for (gid_t group : *extra_groups)
|
|
||||||
{
|
|
||||||
if (metadata.gid == group) return metadata.mode & S_IRGRP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata.mode & S_IROTH;
|
return metadata.mode & S_IROTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,8 +203,7 @@ namespace VFS
|
|||||||
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
||||||
auto new_root_path = TRY(PathParser::basename(new_root));
|
auto new_root_path = TRY(PathParser::basename(new_root));
|
||||||
|
|
||||||
auto new_root_parent_inode =
|
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory));
|
||||||
TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, nullptr, working_directory));
|
|
||||||
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
||||||
|
|
||||||
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -245,7 +215,7 @@ namespace VFS
|
|||||||
|
|
||||||
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, nullptr, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -266,7 +236,7 @@ namespace VFS
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<VFS::Inode> working_directory)
|
SharedPtr<VFS::Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto child = TRY(PathParser::basename(path));
|
||||||
@ -275,7 +245,7 @@ namespace VFS
|
|||||||
kdbgln("vfs: Mounting filesystem on target %s", path);
|
kdbgln("vfs: Mounting filesystem on target %s", path);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -290,8 +260,7 @@ namespace VFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
|
||||||
SharedPtr<VFS::Inode> working_directory)
|
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto child = TRY(PathParser::basename(path));
|
||||||
@ -300,7 +269,7 @@ namespace VFS
|
|||||||
|
|
||||||
kinfoln("vfs: Unmounting filesystem on target %s", path);
|
kinfoln("vfs: Unmounting filesystem on target %s", path);
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (!inode->is_mountpoint()) return err(EINVAL);
|
if (!inode->is_mountpoint()) return err(EINVAL);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "thread/Clock.h"
|
#include "arch/Timer.h"
|
||||||
#include <bits/makedev.h>
|
#include <bits/makedev.h>
|
||||||
#include <bits/timespec.h>
|
#include <bits/timespec.h>
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
@ -27,7 +27,7 @@ namespace VFS
|
|||||||
ino_t inum;
|
ino_t inum;
|
||||||
size_t size { 0 };
|
size_t size { 0 };
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
nlink_t nlinks { 0 };
|
nlink_t nlinks { 1 };
|
||||||
uid_t uid { 0 };
|
uid_t uid { 0 };
|
||||||
gid_t gid { 0 };
|
gid_t gid { 0 };
|
||||||
dev_t devid { 0 };
|
dev_t devid { 0 };
|
||||||
@ -35,19 +35,6 @@ namespace VFS
|
|||||||
struct timespec atime;
|
struct timespec atime;
|
||||||
struct timespec mtime;
|
struct timespec mtime;
|
||||||
struct timespec ctime;
|
struct timespec ctime;
|
||||||
|
|
||||||
void initialize_times()
|
|
||||||
{
|
|
||||||
g_realtime_clock.get_time(atime);
|
|
||||||
g_realtime_clock.get_time(ctime);
|
|
||||||
g_realtime_clock.get_time(mtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_mtime()
|
|
||||||
{
|
|
||||||
g_realtime_clock.get_time(mtime);
|
|
||||||
g_realtime_clock.get_time(ctime);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Inode;
|
class Inode;
|
||||||
@ -131,11 +118,6 @@ namespace VFS
|
|||||||
return err(EACCES);
|
return err(EACCES);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result<SharedPtr<Inode>> open()
|
|
||||||
{
|
|
||||||
return SharedPtr<Inode> { this };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Directory-specific methods
|
// Directory-specific methods
|
||||||
virtual Result<SharedPtr<Inode>> find(const char* name) const = 0;
|
virtual Result<SharedPtr<Inode>> find(const char* name) const = 0;
|
||||||
|
|
||||||
@ -176,7 +158,7 @@ namespace VFS
|
|||||||
virtual Result<void> set_metadata(const InodeMetadata& metadata)
|
virtual Result<void> set_metadata(const InodeMetadata& metadata)
|
||||||
{
|
{
|
||||||
m_metadata = metadata;
|
m_metadata = metadata;
|
||||||
g_realtime_clock.get_time(m_metadata.ctime);
|
m_metadata.ctime = *Timer::realtime_clock();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,23 +301,21 @@ namespace VFS
|
|||||||
virtual ~DeviceInode() = default;
|
virtual ~DeviceInode() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
|
||||||
SharedPtr<VFS::Inode> working_directory = {},
|
SharedPtr<VFS::Inode> working_directory = {},
|
||||||
bool follow_last_symlink = true);
|
bool follow_last_symlink = true);
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<VFS::Inode> working_directory = {});
|
SharedPtr<VFS::Inode> working_directory = {});
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<VFS::Inode> working_directory = {});
|
SharedPtr<VFS::Inode> working_directory = {});
|
||||||
|
|
||||||
Result<void> validate_filename(StringView name);
|
Result<void> validate_filename(StringView name);
|
||||||
|
|
||||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_execute(SharedPtr<Inode> inode, Credentials auth);
|
||||||
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_read(SharedPtr<Inode> inode, Credentials auth);
|
||||||
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_write(SharedPtr<Inode> inode, Credentials auth);
|
||||||
bool is_setuid(SharedPtr<Inode> inode);
|
bool is_setuid(SharedPtr<Inode> inode);
|
||||||
bool is_setgid(SharedPtr<Inode> inode);
|
bool is_setgid(SharedPtr<Inode> inode);
|
||||||
bool is_sticky(SharedPtr<Inode> inode);
|
bool is_sticky(SharedPtr<Inode> inode);
|
||||||
@ -347,8 +327,7 @@ namespace VFS
|
|||||||
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
||||||
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
|
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
|
||||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory = {});
|
|
||||||
|
|
||||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<Inode> working_directory = {});
|
SharedPtr<Inode> working_directory = {});
|
||||||
|
|
||||||
|
Result<void> umount(const char* path, Credentials auth, SharedPtr<Inode> working_directory = {});
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,246 @@
|
|||||||
#include <luna/Units.h>
|
#include <luna/Units.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
|
Vector<SharedPtr<ConsoleDevice>> ConsoleDevice::m_console_devices;
|
||||||
|
bool ConsoleDevice::s_is_in_graphical_mode { false };
|
||||||
|
|
||||||
Result<void> ConsoleDevice::create()
|
Result<void> ConsoleDevice::create()
|
||||||
{
|
{
|
||||||
auto device = TRY(make_shared<ConsoleDevice>());
|
auto device = TRY(make_shared<ConsoleDevice>());
|
||||||
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, 0200);
|
device->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON;
|
||||||
|
device->m_settings.c_cc[VEOF] = '\4';
|
||||||
|
device->m_settings.c_cc[VERASE] = '\b';
|
||||||
|
device->m_settings.c_cc[VINTR] = '\3';
|
||||||
|
device->m_settings.c_cc[VQUIT] = '\x1c';
|
||||||
|
TRY(m_console_devices.try_append(device));
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> ConsoleDevice::read(u8*, usize, usize) const
|
Result<void> ConsoleDevice::handle_background_process_group(bool can_succeed, int signo) const
|
||||||
{
|
{
|
||||||
return 0;
|
if (!m_foreground_process_group.has_value()) return {};
|
||||||
|
|
||||||
|
auto foreground_pgrp = m_foreground_process_group.value();
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
if (current->pgid == foreground_pgrp) return {};
|
||||||
|
|
||||||
|
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||||
|
{
|
||||||
|
if (can_succeed) return {};
|
||||||
|
return err(EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
current->send_signal(signo);
|
||||||
|
|
||||||
|
if (can_succeed) return err(EINTR);
|
||||||
|
return err(EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
||||||
|
{
|
||||||
|
TRY(handle_background_process_group(false, SIGTTIN));
|
||||||
|
|
||||||
|
length = m_input_buffer.dequeue_data(buf, length);
|
||||||
|
|
||||||
|
if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false;
|
||||||
|
|
||||||
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
|
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);
|
TextConsole::write((const char*)buf, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
auto rc = Keyboard::decode_scancode_tty(scancode, m_kb_state);
|
||||||
|
if (!rc.has_value()) return;
|
||||||
|
char key = rc.value();
|
||||||
|
check(key >= 0);
|
||||||
|
|
||||||
|
bool is_special_character { false };
|
||||||
|
|
||||||
|
if (is_canonical())
|
||||||
|
{
|
||||||
|
if (key == m_settings.c_cc[VERASE])
|
||||||
|
{
|
||||||
|
auto maybe_char = m_line_buffer.try_pop();
|
||||||
|
if (maybe_char.has_value() && maybe_char.value())
|
||||||
|
{
|
||||||
|
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
|
||||||
|
{
|
||||||
|
TextConsole::putwchar(L'\b');
|
||||||
|
if (_iscntrl(maybe_char.value())) TextConsole::putwchar(L'\b');
|
||||||
|
if (maybe_char.value() == '\t') TextConsole::wprint(L"\b\b");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_settings.c_lflag & ECHOE)) return;
|
||||||
|
else
|
||||||
|
is_special_character = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == m_settings.c_cc[VEOF])
|
||||||
|
{
|
||||||
|
m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size());
|
||||||
|
m_line_buffer.clear();
|
||||||
|
|
||||||
|
m_may_read_without_blocking = true;
|
||||||
|
is_special_character = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings.c_lflag & ISIG)
|
||||||
|
{
|
||||||
|
if (key == m_settings.c_cc[VINTR])
|
||||||
|
{
|
||||||
|
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
|
||||||
|
|
||||||
|
if (m_foreground_process_group.has_value())
|
||||||
|
{
|
||||||
|
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
|
||||||
|
thread->send_signal(SIGINT);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
is_special_character = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == m_settings.c_cc[VQUIT])
|
||||||
|
{
|
||||||
|
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
|
||||||
|
|
||||||
|
if (m_foreground_process_group.has_value())
|
||||||
|
{
|
||||||
|
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
|
||||||
|
thread->send_signal(SIGQUIT);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
is_special_character = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_special_character)
|
||||||
|
{
|
||||||
|
if (is_canonical())
|
||||||
|
{
|
||||||
|
m_line_buffer.try_append((u8)key);
|
||||||
|
|
||||||
|
if (key == '\n')
|
||||||
|
{
|
||||||
|
m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size());
|
||||||
|
m_line_buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_input_buffer.append_data((u8*)&key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_settings.c_lflag & ECHO)) return;
|
||||||
|
|
||||||
|
if (_iscntrl(key))
|
||||||
|
{
|
||||||
|
if (m_settings.c_lflag & ECHOCTL)
|
||||||
|
{
|
||||||
|
bool should_echo = true;
|
||||||
|
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
|
||||||
|
|
||||||
|
if (should_echo)
|
||||||
|
{
|
||||||
|
char caret_notation[3] = { '^', '\0', '\0' };
|
||||||
|
if (key == 0x7f) caret_notation[1] = '?';
|
||||||
|
else
|
||||||
|
caret_notation[1] = key + 0x40;
|
||||||
|
|
||||||
|
TextConsole::print(caret_notation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
TextConsole::putchar(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
TextConsole::putchar(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> ConsoleDevice::ioctl(int request, void* arg)
|
||||||
|
{
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
TRY(check_pledge(current, Promise::p_tty));
|
||||||
|
|
||||||
|
switch (request)
|
||||||
|
{
|
||||||
|
case TCGETS: {
|
||||||
|
|
||||||
|
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT);
|
||||||
|
}
|
||||||
|
case TCSETS: {
|
||||||
|
TRY(handle_background_process_group(true, SIGTTOU));
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_settings)) return err(EFAULT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case TIOCSPGRP: {
|
||||||
|
TRY(handle_background_process_group(true, SIGTTOU));
|
||||||
|
|
||||||
|
pid_t pgid;
|
||||||
|
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
|
||||||
|
|
||||||
|
bool pgid_exists = false;
|
||||||
|
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
|
||||||
|
pgid_exists = true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (!pgid_exists) return err(EPERM);
|
||||||
|
|
||||||
|
m_foreground_process_group = pgid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case TIOCGPGRP: {
|
||||||
|
pid_t pgid = m_foreground_process_group.value_or((pid_t)next_thread_id());
|
||||||
|
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case TIOCGWINSZ: {
|
||||||
|
struct winsize window;
|
||||||
|
window.ws_col = TextConsole::cols();
|
||||||
|
window.ws_row = TextConsole::rows();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,15 +14,42 @@ class ConsoleDevice : public Device
|
|||||||
|
|
||||||
Result<usize> write(const u8*, usize, usize) override;
|
Result<usize> write(const u8*, usize, usize) override;
|
||||||
|
|
||||||
bool will_block_if_read() const override
|
static void did_press_or_release_key(u8 scancode);
|
||||||
{
|
|
||||||
return false;
|
bool will_block_if_read() const override;
|
||||||
}
|
|
||||||
|
Result<u64> ioctl(int request, void* arg) override;
|
||||||
|
|
||||||
StringView device_path() const override
|
StringView device_path() const override
|
||||||
{
|
{
|
||||||
return "kmsg";
|
return "console";
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> isatty() const override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ConsoleDevice() = default;
|
virtual ~ConsoleDevice() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct termios m_settings;
|
||||||
|
mutable Buffer m_input_buffer;
|
||||||
|
Option<pid_t> m_foreground_process_group {};
|
||||||
|
Vector<u8> m_line_buffer;
|
||||||
|
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);
|
||||||
|
|
||||||
|
mutable bool m_may_read_without_blocking { false };
|
||||||
|
|
||||||
|
inline bool is_canonical() const
|
||||||
|
{
|
||||||
|
return m_settings.c_lflag & ICANON;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> handle_background_process_group(bool can_succeed, int signo) const;
|
||||||
};
|
};
|
||||||
|
@ -7,12 +7,9 @@
|
|||||||
#include "fs/devices/KeyboardDevice.h"
|
#include "fs/devices/KeyboardDevice.h"
|
||||||
#include "fs/devices/MouseDevice.h"
|
#include "fs/devices/MouseDevice.h"
|
||||||
#include "fs/devices/NullDevice.h"
|
#include "fs/devices/NullDevice.h"
|
||||||
#include "fs/devices/PTYMultiplexer.h"
|
|
||||||
#include "fs/devices/TTYLink.h"
|
|
||||||
#include "fs/devices/UARTDevice.h"
|
#include "fs/devices/UARTDevice.h"
|
||||||
#include "fs/devices/ZeroDevice.h"
|
#include "fs/devices/ZeroDevice.h"
|
||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
#include "lib/Mutex.h"
|
|
||||||
#include "thread/Thread.h"
|
#include "thread/Thread.h"
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
@ -27,14 +24,10 @@ struct DeviceDescriptor
|
|||||||
|
|
||||||
Vector<DeviceDescriptor> g_available_devices = {};
|
Vector<DeviceDescriptor> g_available_devices = {};
|
||||||
|
|
||||||
static Mutex g_lock;
|
|
||||||
|
|
||||||
namespace DeviceRegistry
|
namespace DeviceRegistry
|
||||||
{
|
{
|
||||||
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor)
|
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor)
|
||||||
{
|
{
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_available_devices)
|
for (const auto& descriptor : g_available_devices)
|
||||||
{
|
{
|
||||||
if (descriptor.major == major && descriptor.minor == minor) return descriptor.device;
|
if (descriptor.major == major && descriptor.minor == minor) return descriptor.device;
|
||||||
@ -43,7 +36,7 @@ namespace DeviceRegistry
|
|||||||
return err(ENODEV);
|
return err(ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> create_special_device_inode(SharedPtr<VFS::FileSystem> fs, const DeviceDescriptor& descriptor)
|
Result<void> create_special_device_inode(SharedPtr<VFS::FileSystem> fs, const DeviceDescriptor& descriptor)
|
||||||
{
|
{
|
||||||
auto inode = TRY(fs->create_device_inode(descriptor.major, descriptor.minor, descriptor.mode));
|
auto inode = TRY(fs->create_device_inode(descriptor.major, descriptor.minor, descriptor.mode));
|
||||||
TRY(fs->root_inode()->add_entry(inode, descriptor.name));
|
TRY(fs->root_inode()->add_entry(inode, descriptor.name));
|
||||||
@ -53,8 +46,6 @@ namespace DeviceRegistry
|
|||||||
|
|
||||||
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, mode_t mode)
|
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, mode_t mode)
|
||||||
{
|
{
|
||||||
ScopedMutexLock lock(g_lock);
|
|
||||||
|
|
||||||
for (const auto& descriptor : g_available_devices)
|
for (const auto& descriptor : g_available_devices)
|
||||||
{
|
{
|
||||||
if (descriptor.major == major && descriptor.minor == minor) return err(EEXIST);
|
if (descriptor.major == major && descriptor.minor == minor) return err(EEXIST);
|
||||||
@ -89,7 +80,7 @@ namespace DeviceRegistry
|
|||||||
|
|
||||||
dev_t next_null_device_id()
|
dev_t next_null_device_id()
|
||||||
{
|
{
|
||||||
static Atomic<u32> next_minor = 0;
|
static u32 next_minor = 0;
|
||||||
return luna_dev_makedev(0, next_minor++);
|
return luna_dev_makedev(0, next_minor++);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,18 +92,6 @@ namespace DeviceRegistry
|
|||||||
|
|
||||||
for (const auto& descriptor : g_available_devices) TRY(create_special_device_inode(fs, descriptor));
|
for (const auto& descriptor : g_available_devices) TRY(create_special_device_inode(fs, descriptor));
|
||||||
|
|
||||||
auto multiplexer = TRY(make_shared<PTYMultiplexer>());
|
|
||||||
multiplexer->set_fs(*fs);
|
|
||||||
multiplexer->set_inode_number(TRY(fs->allocate_inode_number()));
|
|
||||||
|
|
||||||
TRY(fs->root_inode()->add_entry(multiplexer, "ptmx"));
|
|
||||||
|
|
||||||
auto ttylink = TRY(make_shared<TTYLink>());
|
|
||||||
ttylink->set_fs(*fs);
|
|
||||||
ttylink->set_inode_number(TRY(fs->allocate_inode_number()));
|
|
||||||
|
|
||||||
TRY(fs->root_inode()->add_entry(ttylink, "tty"));
|
|
||||||
|
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ namespace DeviceRegistry
|
|||||||
DiskPartition = 5,
|
DiskPartition = 5,
|
||||||
Serial = 6,
|
Serial = 6,
|
||||||
Input = 7,
|
Input = 7,
|
||||||
Terminal = 8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "video/Framebuffer.h"
|
#include "video/Framebuffer.h"
|
||||||
#include <bits/ioctl-defs.h>
|
#include <bits/ioctl-defs.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/Alignment.h>
|
|
||||||
|
|
||||||
Result<void> FramebufferDevice::create()
|
Result<void> FramebufferDevice::create()
|
||||||
{
|
{
|
||||||
@ -28,7 +27,7 @@ Result<usize> FramebufferDevice::write(const u8* buf, usize offset, usize length
|
|||||||
|
|
||||||
Result<u64> FramebufferDevice::query_shared_memory(off_t offset, usize count)
|
Result<u64> FramebufferDevice::query_shared_memory(off_t offset, usize count)
|
||||||
{
|
{
|
||||||
if (offset + (count * ARCH_PAGE_SIZE) > align_up<ARCH_PAGE_SIZE>((u64)Framebuffer::size())) return err(EINVAL);
|
if (offset + (count * ARCH_PAGE_SIZE) > Framebuffer::size()) return err(EINVAL);
|
||||||
|
|
||||||
if (!m_shmid.has_value())
|
if (!m_shmid.has_value())
|
||||||
{
|
{
|
||||||
|
@ -1,229 +0,0 @@
|
|||||||
#include "fs/devices/MasterPTY.h"
|
|
||||||
#include "Pledge.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
#include "fs/devices/PTYMultiplexer.h"
|
|
||||||
#include "fs/devpts/FileSystem.h"
|
|
||||||
#include "memory/MemoryManager.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include <luna/CType.h>
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
|
|
||||||
{
|
|
||||||
auto master = TRY(make_shared<MasterPTY>());
|
|
||||||
auto slave = TRY(make_shared<SlavePTY>());
|
|
||||||
|
|
||||||
auto name = TRY(String::format("%d"_sv, index));
|
|
||||||
for (auto& fs : g_devpts_instances) { fs->root_inode()->add_entry(slave, name.chars()); }
|
|
||||||
slave->m_name = move(name);
|
|
||||||
|
|
||||||
master->m_metadata.mode = 0666;
|
|
||||||
master->m_index = index;
|
|
||||||
master->m_slave = slave;
|
|
||||||
master->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 0);
|
|
||||||
master->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON;
|
|
||||||
master->m_settings.c_cc[VEOF] = '\4';
|
|
||||||
master->m_settings.c_cc[VERASE] = '\b';
|
|
||||||
master->m_settings.c_cc[VINTR] = '\3';
|
|
||||||
master->m_settings.c_cc[VQUIT] = '\x1c';
|
|
||||||
master->m_window.ws_col = 80;
|
|
||||||
master->m_window.ws_row = 25;
|
|
||||||
master->m_metadata.initialize_times();
|
|
||||||
|
|
||||||
slave->m_master = master.ptr();
|
|
||||||
slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 2);
|
|
||||||
slave->m_metadata.uid = Scheduler::current()->auth.euid;
|
|
||||||
slave->m_metadata.gid = Scheduler::current()->auth.egid;
|
|
||||||
slave->m_metadata.mode = 0620;
|
|
||||||
slave->m_metadata.initialize_times();
|
|
||||||
|
|
||||||
return (SharedPtr<VFS::Inode>)master;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> MasterPTY::handle_background_process_group(bool can_succeed, int signo) const
|
|
||||||
{
|
|
||||||
if (!m_foreground_process_group.has_value()) return {};
|
|
||||||
|
|
||||||
auto foreground_pgrp = m_foreground_process_group.value();
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
if (current->pgid == foreground_pgrp) return {};
|
|
||||||
|
|
||||||
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
|
||||||
{
|
|
||||||
if (can_succeed) return {};
|
|
||||||
return err(EIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
current->send_signal(signo);
|
|
||||||
|
|
||||||
if (can_succeed) return err(EINTR);
|
|
||||||
return err(EIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MasterPTY::is_canonical()
|
|
||||||
{
|
|
||||||
return m_settings.c_lflag & ICANON;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> MasterPTY::handle_input(u8 key)
|
|
||||||
{
|
|
||||||
bool is_special_character { false };
|
|
||||||
|
|
||||||
if (is_canonical())
|
|
||||||
{
|
|
||||||
if (key == m_settings.c_cc[VERASE])
|
|
||||||
{
|
|
||||||
auto maybe_char = m_current_line_buffer.try_pop();
|
|
||||||
if (maybe_char.has_value() && maybe_char.value())
|
|
||||||
{
|
|
||||||
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
|
|
||||||
{
|
|
||||||
u8 backspace = (u8)'\b';
|
|
||||||
m_buffer.append_data(&backspace, 1);
|
|
||||||
if (_iscntrl(maybe_char.value())) m_buffer.append_data(&backspace, 1);
|
|
||||||
if (maybe_char.value() == '\t')
|
|
||||||
{
|
|
||||||
m_buffer.append_data(&backspace, 1);
|
|
||||||
m_buffer.append_data(&backspace, 1);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((m_settings.c_lflag & ECHOE)) return {};
|
|
||||||
else
|
|
||||||
is_special_character = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == m_settings.c_cc[VEOF])
|
|
||||||
{
|
|
||||||
const auto size = m_current_line_buffer.size();
|
|
||||||
auto buffer = Buffer { m_current_line_buffer.release_data(), size };
|
|
||||||
TRY(m_lines.try_append(move(buffer)));
|
|
||||||
|
|
||||||
is_special_character = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_settings.c_lflag & ISIG)
|
|
||||||
{
|
|
||||||
if (key == m_settings.c_cc[VINTR])
|
|
||||||
{
|
|
||||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
|
||||||
|
|
||||||
if (m_foreground_process_group.has_value())
|
|
||||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
|
||||||
thread->send_signal(SIGINT);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
is_special_character = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == m_settings.c_cc[VQUIT])
|
|
||||||
{
|
|
||||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
|
||||||
|
|
||||||
if (m_foreground_process_group.has_value())
|
|
||||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
|
||||||
thread->send_signal(SIGQUIT);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
is_special_character = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_special_character)
|
|
||||||
{
|
|
||||||
if (is_canonical())
|
|
||||||
{
|
|
||||||
const usize max_line_size = key == '\n' ? 4096 : 4095;
|
|
||||||
if (m_current_line_buffer.size() < max_line_size) TRY(m_current_line_buffer.try_append(key));
|
|
||||||
|
|
||||||
if (key == '\n')
|
|
||||||
{
|
|
||||||
const auto size = m_current_line_buffer.size();
|
|
||||||
auto buffer = Buffer { m_current_line_buffer.release_data(), size };
|
|
||||||
TRY(m_lines.try_append(move(buffer)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
TRY(m_slave->m_buffer.append_data(&key, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(m_settings.c_lflag & ECHO)) return {};
|
|
||||||
|
|
||||||
if (_iscntrl(key))
|
|
||||||
{
|
|
||||||
if (m_settings.c_lflag & ECHOCTL)
|
|
||||||
{
|
|
||||||
bool should_echo = true;
|
|
||||||
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
|
|
||||||
|
|
||||||
if (should_echo)
|
|
||||||
{
|
|
||||||
u8 caret_notation[2] = {
|
|
||||||
'^',
|
|
||||||
'\0',
|
|
||||||
};
|
|
||||||
if (key == 0x7f) caret_notation[1] = '?';
|
|
||||||
else
|
|
||||||
caret_notation[1] = key + 0x40;
|
|
||||||
|
|
||||||
m_buffer.append_data(caret_notation, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_buffer.append_data(&key, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_buffer.append_data(&key, 1);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> MasterPTY::read(u8* buf, usize, usize length) const
|
|
||||||
{
|
|
||||||
length = m_buffer.dequeue_data(buf, length);
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < length; i++) { TRY(handle_input(buf[i])); }
|
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> MasterPTY::ioctl(int request, void* arg)
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
TRY(check_pledge(current, Promise::p_tty));
|
|
||||||
|
|
||||||
switch (request)
|
|
||||||
{
|
|
||||||
case TIOCGPTN: {
|
|
||||||
if (!MemoryManager::copy_to_user_typed((int*)arg, &m_index)) return err(EFAULT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default: return err(EINVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MasterPTY::~MasterPTY()
|
|
||||||
{
|
|
||||||
m_slave->m_master = nullptr;
|
|
||||||
for (auto& fs : g_devpts_instances) { fs->root_inode()->remove_entry(m_slave->m_name.chars()); }
|
|
||||||
PTYMultiplexer::did_remove_pty(m_index);
|
|
||||||
|
|
||||||
if (m_session.has_value())
|
|
||||||
{
|
|
||||||
auto leader = Scheduler::find_by_pid(*m_session);
|
|
||||||
if (leader.has_value()) leader.value()->send_signal(SIGHUP);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/SlavePTY.h"
|
|
||||||
#include <bits/termios.h>
|
|
||||||
#include <luna/Buffer.h>
|
|
||||||
|
|
||||||
class MasterPTY : public VFS::DeviceInode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MasterPTY() = default;
|
|
||||||
|
|
||||||
static Result<SharedPtr<VFS::Inode>> create_pair(int index);
|
|
||||||
|
|
||||||
VFS::InodeType type() const override
|
|
||||||
{
|
|
||||||
return VFS::InodeType::CharacterDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> query_shared_memory(off_t, usize) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
VFS::FileSystem* fs() const override
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> read(u8* buf, usize offset, usize length) const override;
|
|
||||||
|
|
||||||
Result<usize> write(const u8* buf, usize offset, usize length) override;
|
|
||||||
|
|
||||||
Result<u64> ioctl(int request, void* arg) override;
|
|
||||||
|
|
||||||
Result<u64> isatty() const override
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> truncate(usize) override
|
|
||||||
{
|
|
||||||
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
|
|
||||||
return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool will_block_if_read() const override
|
|
||||||
{
|
|
||||||
return m_buffer.is_empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_link() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_unlink() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~MasterPTY();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct termios m_settings;
|
|
||||||
mutable Buffer m_buffer;
|
|
||||||
SharedPtr<SlavePTY> m_slave;
|
|
||||||
mutable Option<pid_t> m_foreground_process_group;
|
|
||||||
mutable Option<pid_t> m_session;
|
|
||||||
struct winsize m_window;
|
|
||||||
|
|
||||||
typedef Vector<u8> Line;
|
|
||||||
|
|
||||||
Vector<Buffer> m_lines;
|
|
||||||
Line m_current_line_buffer;
|
|
||||||
|
|
||||||
Result<void> handle_background_process_group(bool can_succeed, int signo) const;
|
|
||||||
Result<void> handle_input(u8 key);
|
|
||||||
|
|
||||||
bool is_canonical();
|
|
||||||
|
|
||||||
int m_index;
|
|
||||||
|
|
||||||
friend class SlavePTY;
|
|
||||||
};
|
|
@ -1,37 +0,0 @@
|
|||||||
#include "fs/devices/PTYMultiplexer.h"
|
|
||||||
|
|
||||||
Bitset<u64> PTYMultiplexer::m_available_indexes = 0;
|
|
||||||
|
|
||||||
PTYMultiplexer::PTYMultiplexer()
|
|
||||||
{
|
|
||||||
m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 0);
|
|
||||||
m_metadata.mode = 0666;
|
|
||||||
m_metadata.initialize_times();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> PTYMultiplexer::open()
|
|
||||||
{
|
|
||||||
int index = -1;
|
|
||||||
for (int i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
if (!m_available_indexes.get(i))
|
|
||||||
{
|
|
||||||
index = i;
|
|
||||||
m_available_indexes.set(i, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index == -1) return err(ENOSPC);
|
|
||||||
|
|
||||||
return MasterPTY::create_pair(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PTYMultiplexer::init()
|
|
||||||
{
|
|
||||||
m_available_indexes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PTYMultiplexer::did_remove_pty(int index)
|
|
||||||
{
|
|
||||||
m_available_indexes.set(index, false);
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
#include "fs/devices/MasterPTY.h"
|
|
||||||
#include <luna/Bitset.h>
|
|
||||||
|
|
||||||
class PTYMultiplexer : public VFS::DeviceInode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PTYMultiplexer();
|
|
||||||
|
|
||||||
VFS::InodeType type() const override
|
|
||||||
{
|
|
||||||
return VFS::InodeType::CharacterDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_fs(VFS::FileSystem& fs)
|
|
||||||
{
|
|
||||||
m_fs = &fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_inode_number(usize inum)
|
|
||||||
{
|
|
||||||
m_metadata.inum = inum;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> query_shared_memory(off_t, usize) override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> open() override;
|
|
||||||
|
|
||||||
VFS::FileSystem* fs() const override
|
|
||||||
{
|
|
||||||
return m_fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> write(const u8*, usize, usize) override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> truncate(usize) override
|
|
||||||
{
|
|
||||||
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
|
|
||||||
return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool will_block_if_read() const override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_link() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_unlink() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks--;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init();
|
|
||||||
|
|
||||||
static void did_remove_pty(int index);
|
|
||||||
|
|
||||||
virtual ~PTYMultiplexer() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VFS::FileSystem* m_fs;
|
|
||||||
|
|
||||||
static Bitset<u64> m_available_indexes;
|
|
||||||
};
|
|
@ -1,110 +0,0 @@
|
|||||||
#include "fs/devices/SlavePTY.h"
|
|
||||||
#include "Pledge.h"
|
|
||||||
#include "fs/devices/MasterPTY.h"
|
|
||||||
#include "memory/MemoryManager.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
|
|
||||||
Result<usize> SlavePTY::read(u8* buf, usize, usize length) const
|
|
||||||
{
|
|
||||||
if (!m_master) return err(EIO);
|
|
||||||
|
|
||||||
TRY(m_master->handle_background_process_group(false, SIGTTIN));
|
|
||||||
|
|
||||||
if (!m_master->is_canonical()) length = m_buffer.dequeue_data(buf, length);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_master->m_lines.size() == 0) { return 0; }
|
|
||||||
|
|
||||||
auto& line = m_master->m_lines[0];
|
|
||||||
length = line.dequeue_data(buf, length);
|
|
||||||
if (line.size() == 0) m_master->m_lines.remove_at(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> SlavePTY::write(const u8* buf, usize, usize length)
|
|
||||||
{
|
|
||||||
if (!m_master) return err(EIO);
|
|
||||||
|
|
||||||
if (m_master->m_settings.c_lflag & TOSTOP) TRY(m_master->handle_background_process_group(true, SIGTTOU));
|
|
||||||
|
|
||||||
TRY(m_master->m_buffer.append_data(buf, length));
|
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SlavePTY::will_block_if_read() const
|
|
||||||
{
|
|
||||||
if (!m_master) return false;
|
|
||||||
|
|
||||||
if (!m_master->is_canonical()) return m_buffer.is_empty();
|
|
||||||
else
|
|
||||||
return !m_master->m_lines.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> SlavePTY::ioctl(int request, void* arg)
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
TRY(check_pledge(current, Promise::p_tty));
|
|
||||||
|
|
||||||
if (!m_master) return err(EIO);
|
|
||||||
|
|
||||||
switch (request)
|
|
||||||
{
|
|
||||||
case TCGETS: {
|
|
||||||
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_master->m_settings) ? 0 : err(EFAULT);
|
|
||||||
}
|
|
||||||
case TCSETS: {
|
|
||||||
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_master->m_settings)) return err(EFAULT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCSPGRP: {
|
|
||||||
pid_t pgid;
|
|
||||||
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
|
|
||||||
if (current->controlling_terminal != SharedPtr<VFS::Inode> { this }) return err(ENOTTY);
|
|
||||||
|
|
||||||
bool pgid_exists = false;
|
|
||||||
pid_t sid;
|
|
||||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* thread) {
|
|
||||||
pgid_exists = true;
|
|
||||||
sid = thread->sid; // should be the same for all threads in the process group
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (!pgid_exists) return err(EPERM);
|
|
||||||
if (sid != current->sid) return err(EPERM);
|
|
||||||
|
|
||||||
m_master->m_foreground_process_group = pgid;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCGPGRP: {
|
|
||||||
if (current->controlling_terminal != SharedPtr<VFS::Inode> { this }) return err(ENOTTY);
|
|
||||||
pid_t pgid = m_master->m_foreground_process_group.value_or((pid_t)next_thread_id());
|
|
||||||
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCGWINSZ: {
|
|
||||||
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &m_master->m_window)) return err(EFAULT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCSCTTY: {
|
|
||||||
if (current->controlling_terminal) return err(EPERM);
|
|
||||||
if (this->m_master->m_session.has_value()) return err(EPERM);
|
|
||||||
if (!current->is_session_leader()) return err(EPERM);
|
|
||||||
|
|
||||||
Scheduler::for_each_in_session(current->sid, [this](Thread* thread) {
|
|
||||||
thread->controlling_terminal = this;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
m_master->m_session = current->sid;
|
|
||||||
m_master->m_foreground_process_group = current->pgid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default: return err(EINVAL);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include <luna/Buffer.h>
|
|
||||||
#include <luna/String.h>
|
|
||||||
|
|
||||||
class MasterPTY;
|
|
||||||
|
|
||||||
class SlavePTY : public VFS::DeviceInode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SlavePTY() = default;
|
|
||||||
|
|
||||||
VFS::InodeType type() const override
|
|
||||||
{
|
|
||||||
return VFS::InodeType::CharacterDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> query_shared_memory(off_t, usize) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
VFS::FileSystem* fs() const override
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> read(u8* buf, usize offset, usize length) const override;
|
|
||||||
|
|
||||||
Result<usize> write(const u8* buf, usize offset, usize length) override;
|
|
||||||
|
|
||||||
Result<u64> ioctl(int request, void* arg) override;
|
|
||||||
|
|
||||||
Result<u64> isatty() const override
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> truncate(usize) override
|
|
||||||
{
|
|
||||||
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
|
|
||||||
return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool will_block_if_read() const override;
|
|
||||||
|
|
||||||
void did_link() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_unlink() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~SlavePTY() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable Buffer m_buffer;
|
|
||||||
|
|
||||||
MasterPTY* m_master;
|
|
||||||
String m_name;
|
|
||||||
|
|
||||||
friend class MasterPTY;
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
#include "fs/devices/TTYLink.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
|
|
||||||
TTYLink::TTYLink()
|
|
||||||
{
|
|
||||||
m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 1);
|
|
||||||
m_metadata.mode = 0666;
|
|
||||||
m_metadata.initialize_times();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> TTYLink::open()
|
|
||||||
{
|
|
||||||
if (!Scheduler::current()->controlling_terminal) return err(ENXIO);
|
|
||||||
|
|
||||||
return Scheduler::current()->controlling_terminal;
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
#include "fs/devices/MasterPTY.h"
|
|
||||||
#include <luna/Bitset.h>
|
|
||||||
|
|
||||||
/* /dev/tty implementation. */
|
|
||||||
class TTYLink : public VFS::DeviceInode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TTYLink();
|
|
||||||
|
|
||||||
VFS::InodeType type() const override
|
|
||||||
{
|
|
||||||
return VFS::InodeType::CharacterDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_fs(VFS::FileSystem& fs)
|
|
||||||
{
|
|
||||||
m_fs = &fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_inode_number(usize inum)
|
|
||||||
{
|
|
||||||
m_metadata.inum = inum;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> query_shared_memory(off_t, usize) override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> open() override;
|
|
||||||
|
|
||||||
VFS::FileSystem* fs() const override
|
|
||||||
{
|
|
||||||
return m_fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> write(const u8*, usize, usize) override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> truncate(usize) override
|
|
||||||
{
|
|
||||||
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
|
|
||||||
return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool will_block_if_read() const override
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_link() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_unlink() override
|
|
||||||
{
|
|
||||||
m_metadata.nlinks--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~TTYLink() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VFS::FileSystem* m_fs;
|
|
||||||
};
|
|
@ -1,61 +0,0 @@
|
|||||||
#include "fs/devpts/FileSystem.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
#include "fs/devpts/Inode.h"
|
|
||||||
#include <luna/Alloc.h>
|
|
||||||
#include <luna/CString.h>
|
|
||||||
#include <luna/Ignore.h>
|
|
||||||
|
|
||||||
Vector<VFS::FileSystem*> g_devpts_instances;
|
|
||||||
|
|
||||||
namespace DevPTS
|
|
||||||
{
|
|
||||||
Result<SharedPtr<VFS::FileSystem>> FileSystem::create()
|
|
||||||
{
|
|
||||||
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
|
|
||||||
SharedPtr<RootInode> root = TRY(make_shared<RootInode>());
|
|
||||||
|
|
||||||
TRY(root->add_entry(root, "."));
|
|
||||||
TRY(root->add_entry(root, ".."));
|
|
||||||
|
|
||||||
root->set_self(root, {});
|
|
||||||
root->set_fs(*fs, {});
|
|
||||||
root->set_inode_number();
|
|
||||||
root->m_metadata.mode = 0755;
|
|
||||||
root->m_metadata.initialize_times();
|
|
||||||
fs->set_root(root);
|
|
||||||
|
|
||||||
TRY(g_devpts_instances.try_append(fs.ptr()));
|
|
||||||
|
|
||||||
return (SharedPtr<VFS::FileSystem>)fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> FileSystem::allocate_inode_number()
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystem::FileSystem()
|
|
||||||
{
|
|
||||||
m_host_device_id = DeviceRegistry::next_null_device_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
|
|
||||||
{
|
|
||||||
return m_root_inode->replace_entry(parent, "..");
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> FileSystem::reset_mount_dir()
|
|
||||||
{
|
|
||||||
return m_root_inode->replace_entry(m_root_inode, "..");
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
|
|
||||||
{
|
|
||||||
m_root_inode = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystem::~FileSystem()
|
|
||||||
{
|
|
||||||
g_devpts_instances.remove_first_matching([this](VFS::FileSystem* ptr) { return ptr == this; });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
|
|
||||||
namespace DevPTS
|
|
||||||
{
|
|
||||||
class FileSystem : public VFS::FileSystem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SharedPtr<VFS::Inode> root_inode() const override
|
|
||||||
{
|
|
||||||
return m_root_inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_file_inode(mode_t) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>, mode_t) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32, mode_t) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView) override
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> allocate_inode_number() override;
|
|
||||||
|
|
||||||
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
|
|
||||||
|
|
||||||
Result<void> reset_mount_dir() override;
|
|
||||||
|
|
||||||
static Result<SharedPtr<VFS::FileSystem>> create();
|
|
||||||
|
|
||||||
dev_t host_device_id() const override
|
|
||||||
{
|
|
||||||
return m_host_device_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~FileSystem();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FileSystem();
|
|
||||||
|
|
||||||
void set_root(SharedPtr<VFS::Inode> root);
|
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> m_root_inode;
|
|
||||||
|
|
||||||
dev_t m_host_device_id;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern Vector<VFS::FileSystem*> g_devpts_instances;
|
|
@ -1,78 +0,0 @@
|
|||||||
#include "fs/devpts/Inode.h"
|
|
||||||
|
|
||||||
namespace DevPTS
|
|
||||||
{
|
|
||||||
Result<SharedPtr<VFS::Inode>> RootInode::find(const char* name) const
|
|
||||||
{
|
|
||||||
for (const auto& entry : m_entries)
|
|
||||||
{
|
|
||||||
if (!strcmp(name, entry.name.chars())) return entry.inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> RootInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
|
|
||||||
{
|
|
||||||
for (auto& entry : m_entries)
|
|
||||||
{
|
|
||||||
if (!strcmp(name, entry.name.chars()))
|
|
||||||
{
|
|
||||||
entry.inode = inode;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
Option<VFS::DirectoryEntry> RootInode::get(usize index) const
|
|
||||||
{
|
|
||||||
if (index >= m_entries.size()) return {};
|
|
||||||
|
|
||||||
return m_entries[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> RootInode::add_entry(SharedPtr<VFS::Inode> inode, const char* name)
|
|
||||||
{
|
|
||||||
if (find(name).has_value()) return err(EEXIST);
|
|
||||||
|
|
||||||
VFS::DirectoryEntry entry { inode, name };
|
|
||||||
|
|
||||||
TRY(m_entries.try_append(move(entry)));
|
|
||||||
|
|
||||||
inode->did_link();
|
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> RootInode::remove_entry(const char* name)
|
|
||||||
{
|
|
||||||
SharedPtr<VFS::Inode> inode = TRY(find(name));
|
|
||||||
|
|
||||||
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
|
|
||||||
|
|
||||||
if (inode->is_mountpoint()) return err(EBUSY);
|
|
||||||
|
|
||||||
m_entries.remove_first_matching(
|
|
||||||
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });
|
|
||||||
|
|
||||||
inode->did_unlink();
|
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> RootInode::create_file(const char*, mode_t)
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> RootInode::create_subdirectory(const char*, mode_t)
|
|
||||||
{
|
|
||||||
return err(ENOTSUP);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
|
||||||
#include "fs/devpts/FileSystem.h"
|
|
||||||
|
|
||||||
namespace DevPTS
|
|
||||||
{
|
|
||||||
class RootInode : public VFS::Inode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RootInode() = default;
|
|
||||||
|
|
||||||
void set_fs(FileSystem& fs, Badge<FileSystem>)
|
|
||||||
{
|
|
||||||
m_fs = &fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_inode_number()
|
|
||||||
{
|
|
||||||
m_metadata.inum = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_self(SharedPtr<VFS::Inode> self, Badge<FileSystem>)
|
|
||||||
{
|
|
||||||
m_self = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> find(const char* name) const override;
|
|
||||||
Option<VFS::DirectoryEntry> get(usize index) const override;
|
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override
|
|
||||||
{
|
|
||||||
return err(EISDIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> write(const u8*, usize, usize) override
|
|
||||||
{
|
|
||||||
return err(EISDIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> truncate(usize) override
|
|
||||||
{
|
|
||||||
return err(EISDIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool will_block_if_read() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
VFS::FileSystem* fs() const override
|
|
||||||
{
|
|
||||||
return m_fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
VFS::InodeType type() const override
|
|
||||||
{
|
|
||||||
return VFS::InodeType::Directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_link() override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void did_unlink() override
|
|
||||||
{
|
|
||||||
m_self = {};
|
|
||||||
m_entries.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
usize entries() const override
|
|
||||||
{
|
|
||||||
return m_entries.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> remove_entry(const char* name) override;
|
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override;
|
|
||||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override;
|
|
||||||
|
|
||||||
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
|
||||||
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
|
||||||
|
|
||||||
virtual ~RootInode() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VFS::FileSystem* m_fs;
|
|
||||||
SharedPtr<VFS::Inode> m_self;
|
|
||||||
Vector<VFS::DirectoryEntry> m_entries;
|
|
||||||
|
|
||||||
friend class FileSystem;
|
|
||||||
};
|
|
||||||
}
|
|
@ -7,13 +7,6 @@ namespace Ext2
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> Inode::prepare_buffer() const
|
|
||||||
{
|
|
||||||
if (!m_block_buffer.is_empty()) return {};
|
|
||||||
return m_block_buffer.try_resize(m_fs->m_block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This code seems awfully similar to BlockDevice::read(). Can this be merged with it in some way?
|
|
||||||
Result<usize> Inode::read(u8* buf, usize offset, usize length) const
|
Result<usize> Inode::read(u8* buf, usize offset, usize length) const
|
||||||
{
|
{
|
||||||
if (length == 0) return 0;
|
if (length == 0) return 0;
|
||||||
@ -73,47 +66,22 @@ namespace Ext2
|
|||||||
{
|
{
|
||||||
if (index < 12) return m_raw_inode.direct_pointers[index];
|
if (index < 12) return m_raw_inode.direct_pointers[index];
|
||||||
|
|
||||||
const usize block_size = m_fs->m_block_size;
|
usize block_index = (index - 12) * sizeof(u32);
|
||||||
const usize blocks_per_indirect_block = block_size / sizeof(u32);
|
if (block_index >= m_fs->m_block_size)
|
||||||
|
|
||||||
index -= 12;
|
|
||||||
|
|
||||||
TRY(prepare_buffer());
|
|
||||||
|
|
||||||
// Singly indirect block
|
|
||||||
if (index < blocks_per_indirect_block)
|
|
||||||
{
|
{
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), m_raw_inode.singly_indirect_ptr * block_size,
|
fail("ext2: Finding blocks beyond the singly indirect pointer block is not yet supported");
|
||||||
block_size));
|
|
||||||
return ((u32*)m_block_buffer.data())[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index -= blocks_per_indirect_block;
|
usize block_size = m_fs->m_block_size;
|
||||||
|
|
||||||
// Doubly indirect block
|
if (m_singly_indirect_block.is_empty())
|
||||||
if (index < blocks_per_indirect_block * blocks_per_indirect_block)
|
|
||||||
{
|
{
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), m_raw_inode.doubly_indirect_ptr * block_size,
|
TRY(m_singly_indirect_block.try_resize(block_size));
|
||||||
|
TRY(m_fs->m_host_device->read(m_singly_indirect_block.data(), m_raw_inode.singly_indirect_ptr * block_size,
|
||||||
block_size));
|
block_size));
|
||||||
const u32 block = ((u32*)m_block_buffer.data())[index / blocks_per_indirect_block];
|
|
||||||
|
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), block * block_size, block_size));
|
|
||||||
return ((u32*)m_block_buffer.data())[index % blocks_per_indirect_block];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index -= blocks_per_indirect_block * blocks_per_indirect_block;
|
return *reinterpret_cast<u32*>(&m_singly_indirect_block.data()[block_index]);
|
||||||
|
|
||||||
check(index < blocks_per_indirect_block * blocks_per_indirect_block * blocks_per_indirect_block);
|
|
||||||
|
|
||||||
// Triply indirect block
|
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), m_raw_inode.triply_indirect_ptr * block_size, block_size));
|
|
||||||
u32 block = ((u32*)m_block_buffer.data())[index / (blocks_per_indirect_block * blocks_per_indirect_block)];
|
|
||||||
|
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), block * block_size, block_size));
|
|
||||||
block = ((u32*)m_block_buffer.data())[(index / blocks_per_indirect_block) % blocks_per_indirect_block];
|
|
||||||
|
|
||||||
TRY(m_fs->m_host_device->read(m_block_buffer.data(), block * block_size, block_size));
|
|
||||||
return ((u32*)m_block_buffer.data())[index % blocks_per_indirect_block];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> Inode::lazy_initialize_dir() const
|
Result<void> Inode::lazy_initialize_dir() const
|
||||||
|
@ -115,7 +115,7 @@ namespace Ext2
|
|||||||
FileSystem* m_fs;
|
FileSystem* m_fs;
|
||||||
ino_t m_inum;
|
ino_t m_inum;
|
||||||
|
|
||||||
mutable Buffer m_block_buffer;
|
mutable Buffer m_singly_indirect_block;
|
||||||
|
|
||||||
String m_link;
|
String m_link;
|
||||||
|
|
||||||
@ -123,7 +123,6 @@ namespace Ext2
|
|||||||
mutable bool m_dir_already_lazily_initialized { false };
|
mutable bool m_dir_already_lazily_initialized { false };
|
||||||
|
|
||||||
Result<usize> find_block(usize index) const;
|
Result<usize> find_block(usize index) const;
|
||||||
Result<void> prepare_buffer() const;
|
|
||||||
|
|
||||||
friend class FileSystem;
|
friend class FileSystem;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
|
#include "arch/Timer.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "fs/tmpfs/Inode.h"
|
#include "fs/tmpfs/Inode.h"
|
||||||
#include "thread/Clock.h"
|
|
||||||
#include <luna/Alloc.h>
|
#include <luna/Alloc.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/Ignore.h>
|
#include <luna/Ignore.h>
|
||||||
@ -27,7 +27,7 @@ namespace TmpFS
|
|||||||
inode->set_fs(*this, {});
|
inode->set_fs(*this, {});
|
||||||
inode->set_inode_number(m_next_inode_number++, {});
|
inode->set_inode_number(m_next_inode_number++, {});
|
||||||
inode->m_metadata.mode = mode;
|
inode->m_metadata.mode = mode;
|
||||||
inode->m_metadata.initialize_times();
|
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
|
||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ namespace TmpFS
|
|||||||
inode->set_fs(*this, {});
|
inode->set_fs(*this, {});
|
||||||
TRY(inode->set_link(link, {}));
|
TRY(inode->set_link(link, {}));
|
||||||
inode->set_inode_number(m_next_inode_number++, {});
|
inode->set_inode_number(m_next_inode_number++, {});
|
||||||
inode->m_metadata.initialize_times();
|
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
|
||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ namespace TmpFS
|
|||||||
inode->set_fs(*this, {});
|
inode->set_fs(*this, {});
|
||||||
inode->set_inode_number(m_next_inode_number++, {});
|
inode->set_inode_number(m_next_inode_number++, {});
|
||||||
inode->m_metadata.mode = mode;
|
inode->m_metadata.mode = mode;
|
||||||
inode->m_metadata.initialize_times();
|
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
|
||||||
|
|
||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ namespace TmpFS
|
|||||||
// device ID atm.
|
// device ID atm.
|
||||||
inode->set_device_id(luna_dev_makedev(major, minor), {});
|
inode->set_device_id(luna_dev_makedev(major, minor), {});
|
||||||
inode->m_metadata.mode = mode;
|
inode->m_metadata.mode = mode;
|
||||||
inode->m_metadata.initialize_times();
|
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
|
||||||
inode->m_metadata.size = device->size();
|
inode->m_metadata.size = device->size();
|
||||||
|
|
||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
|
@ -45,7 +45,7 @@ namespace TmpFS
|
|||||||
|
|
||||||
inode->did_link();
|
inode->did_link();
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
m_metadata.mtime = *Timer::realtime_clock();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ namespace TmpFS
|
|||||||
|
|
||||||
inode->did_unlink();
|
inode->did_unlink();
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
m_metadata.mtime = *Timer::realtime_clock();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ namespace TmpFS
|
|||||||
|
|
||||||
m_metadata.size = m_data_buffer.size();
|
m_metadata.size = m_data_buffer.size();
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
m_metadata.mtime = *Timer::realtime_clock();
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ namespace TmpFS
|
|||||||
|
|
||||||
m_metadata.size = m_data_buffer.size();
|
m_metadata.size = m_data_buffer.size();
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
m_metadata.mtime = *Timer::realtime_clock();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -269,12 +269,10 @@ namespace TmpFS
|
|||||||
|
|
||||||
void did_link() override
|
void did_link() override
|
||||||
{
|
{
|
||||||
m_metadata.nlinks++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void did_unlink() override
|
void did_unlink() override
|
||||||
{
|
{
|
||||||
m_metadata.nlinks--;
|
|
||||||
m_self = {};
|
m_self = {};
|
||||||
m_entries.clear();
|
m_entries.clear();
|
||||||
}
|
}
|
||||||
|
77
kernel/src/lib/KMutex.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Log.h"
|
||||||
|
#include "arch/CPU.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
#include "thread/Thread.h"
|
||||||
|
#include <luna/CircularQueue.h>
|
||||||
|
|
||||||
|
template <usize ConcurrentThreads> class KMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
int expected = 0;
|
||||||
|
while (!m_lock.compare_exchange_strong(expected, 1))
|
||||||
|
{
|
||||||
|
expected = 0;
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
// We cannot be interrupted between these functions, otherwise we might never exit the loop
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
bool ok = m_blocked_threads.try_push(current);
|
||||||
|
if (!ok) kernel_sleep(10);
|
||||||
|
else
|
||||||
|
kernel_wait_for_event();
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
int expected = 1;
|
||||||
|
if (!m_lock.compare_exchange_strong(expected, 0))
|
||||||
|
{
|
||||||
|
kwarnln("KMutex::unlock() called on an unlocked lock with value %d", expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* blocked;
|
||||||
|
if (m_blocked_threads.try_pop(blocked)) blocked->wake_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock()
|
||||||
|
{
|
||||||
|
int expected = 0;
|
||||||
|
return m_lock.compare_exchange_strong(expected, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CircularQueue<Thread*, ConcurrentThreads> m_blocked_threads;
|
||||||
|
Atomic<int> m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <usize ConcurrentThreads> class ScopedKMutexLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopedKMutexLock(KMutex<ConcurrentThreads>& lock) : m_lock(lock)
|
||||||
|
{
|
||||||
|
m_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedKMutexLock()
|
||||||
|
{
|
||||||
|
if (!m_taken_over) m_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedKMutexLock(const ScopedKMutexLock&) = delete;
|
||||||
|
ScopedKMutexLock(ScopedKMutexLock&&) = delete;
|
||||||
|
|
||||||
|
KMutex<ConcurrentThreads>& take_over()
|
||||||
|
{
|
||||||
|
m_taken_over = true;
|
||||||
|
return m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
KMutex<ConcurrentThreads>& m_lock;
|
||||||
|
bool m_taken_over { false };
|
||||||
|
};
|
@ -1,93 +0,0 @@
|
|||||||
#include "lib/Mutex.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "arch/CPU.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
|
|
||||||
void Mutex::lock()
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
const pid_t desired = current->id;
|
|
||||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// Make sure only one thread is touching the mutex at the same time.
|
|
||||||
m_spinlock.lock();
|
|
||||||
|
|
||||||
pid_t expected = 0;
|
|
||||||
if (!m_thread.compare_exchange_strong(expected, desired))
|
|
||||||
{
|
|
||||||
if (expected == desired)
|
|
||||||
{
|
|
||||||
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->id);
|
|
||||||
fail("Mutex deadlock detected");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = m_blocked_threads.try_push(current);
|
|
||||||
m_spinlock.unlock();
|
|
||||||
if (!ok) kernel_sleep(10);
|
|
||||||
else
|
|
||||||
kernel_wait_for_event();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_spinlock.unlock();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Mutex::unlock()
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
pid_t expected = current->id;
|
|
||||||
check(expected > 0); // Why the hell would the idle thread be touching a mutex?
|
|
||||||
|
|
||||||
m_spinlock.lock();
|
|
||||||
|
|
||||||
if (!m_thread.compare_exchange_strong(expected, 0))
|
|
||||||
{
|
|
||||||
kerrorln("KMutex::unlock() called on a lock already locked by another thread (%d, current is %d)", expected,
|
|
||||||
current->id);
|
|
||||||
fail("Mutex unlock by different thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread* blocked;
|
|
||||||
if (m_blocked_threads.try_pop(blocked))
|
|
||||||
{
|
|
||||||
while (blocked->state == ThreadState::Runnable)
|
|
||||||
{
|
|
||||||
// Rarely, we could switch to here between m_spinlock.unlock() and kernel_wait_for_event() above.
|
|
||||||
// Let the thread go to sleep before waking it up again.
|
|
||||||
kernel_yield();
|
|
||||||
}
|
|
||||||
blocked->wake_up();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_spinlock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Mutex::try_lock()
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
const pid_t desired = current->id;
|
|
||||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
|
||||||
|
|
||||||
// Make sure only one thread is touching the mutex at the same time.
|
|
||||||
m_spinlock.lock();
|
|
||||||
|
|
||||||
pid_t expected = 0;
|
|
||||||
bool ok = m_thread.compare_exchange_strong(expected, desired);
|
|
||||||
|
|
||||||
if (expected == desired)
|
|
||||||
{
|
|
||||||
kwarnln("Deadlock avoided! KMutex::try_lock() failed because it was already locked by the same thread "
|
|
||||||
"(%d), this is not supposed to happen",
|
|
||||||
current->id);
|
|
||||||
CPU::print_stack_trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_spinlock.unlock();
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "thread/Thread.h"
|
|
||||||
#include <luna/CircularQueue.h>
|
|
||||||
#include <luna/Spinlock.h>
|
|
||||||
|
|
||||||
class Mutex
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void lock();
|
|
||||||
void unlock();
|
|
||||||
bool try_lock();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CircularQueue<Thread*, 32> m_blocked_threads;
|
|
||||||
Spinlock m_spinlock;
|
|
||||||
Atomic<pid_t> m_thread;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScopedMutexLock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ScopedMutexLock(Mutex& lock) : m_lock(lock)
|
|
||||||
{
|
|
||||||
m_lock.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedMutexLock()
|
|
||||||
{
|
|
||||||
if (!m_taken_over) m_lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedMutexLock(const ScopedMutexLock&) = delete;
|
|
||||||
ScopedMutexLock(ScopedMutexLock&&) = delete;
|
|
||||||
|
|
||||||
Mutex& take_over()
|
|
||||||
{
|
|
||||||
m_taken_over = true;
|
|
||||||
return m_lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Mutex& m_lock;
|
|
||||||
bool m_taken_over { false };
|
|
||||||
};
|
|
@ -1,16 +1,13 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Symbols.h"
|
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
|
#include "arch/Timer.h"
|
||||||
#include "binfmt/BinaryFormat.h"
|
#include "binfmt/BinaryFormat.h"
|
||||||
#include "boot/Init.h"
|
#include "boot/Init.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fs/FSRegistry.h"
|
|
||||||
#include "fs/InitRD.h"
|
#include "fs/InitRD.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "fs/devices/PTYMultiplexer.h"
|
|
||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "thread/Clock.h"
|
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include <luna/Units.h>
|
#include <luna/Units.h>
|
||||||
|
|
||||||
@ -55,19 +52,16 @@ void oom_thread()
|
|||||||
kinfoln("Current platform: %s", CPU::platform_string().chars());
|
kinfoln("Current platform: %s", CPU::platform_string().chars());
|
||||||
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());
|
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());
|
||||||
|
|
||||||
Symbols::load();
|
|
||||||
|
|
||||||
auto root = mark_critical(TmpFS::FileSystem::create(), "Failed to create initial ramfs");
|
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(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(InitRD::populate_vfs(), "Failed to load files from the initial ramdisk");
|
||||||
mark_critical(DeviceRegistry::init(), "Failed to register initial devices");
|
mark_critical(DeviceRegistry::init(), "Failed to register initial devices");
|
||||||
|
|
||||||
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
||||||
mark_critical(FSRegistry::init(), "Failed to register initial file systems");
|
|
||||||
|
|
||||||
auto init = mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}, nullptr),
|
auto init =
|
||||||
"Can't find init in the initial ramfs!");
|
mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}), "Can't find init in the initial ramfs!");
|
||||||
auto init_thread = mark_critical(Scheduler::create_init_process(init, "/bin/preinit"),
|
auto init_thread = mark_critical(Scheduler::new_userspace_thread(init, "/bin/preinit"),
|
||||||
"Failed to create PID 1 process for init");
|
"Failed to create PID 1 process for init");
|
||||||
|
|
||||||
auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
|
auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
|
||||||
@ -96,11 +90,10 @@ extern "C" [[noreturn]] void _start()
|
|||||||
Init::check_magic();
|
Init::check_magic();
|
||||||
Init::early_init();
|
Init::early_init();
|
||||||
|
|
||||||
Clock::init();
|
Timer::init();
|
||||||
|
|
||||||
Thread::init();
|
Thread::init();
|
||||||
Scheduler::init();
|
Scheduler::init();
|
||||||
PTYMultiplexer::init();
|
|
||||||
|
|
||||||
Scheduler::new_kernel_thread(init, "[kinit]");
|
Scheduler::new_kernel_thread(init, "[kinit]");
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "fs/StorageCache.h"
|
#include "fs/StorageCache.h"
|
||||||
#include "memory/KernelVM.h"
|
#include "memory/KernelVM.h"
|
||||||
#include "memory/MemoryMap.h"
|
#include "memory/MemoryMap.h"
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Bitmap.h>
|
#include <luna/Bitmap.h>
|
||||||
#include <luna/Common.h>
|
#include <luna/Common.h>
|
||||||
@ -157,18 +156,6 @@ namespace MemoryManager
|
|||||||
used_mem += ARCH_PAGE_SIZE;
|
used_mem += ARCH_PAGE_SIZE;
|
||||||
free_mem -= ARCH_PAGE_SIZE;
|
free_mem -= ARCH_PAGE_SIZE;
|
||||||
|
|
||||||
if (free_mem * 4 == total())
|
|
||||||
{
|
|
||||||
kwarnln("Only 1/4 of memory is free, clearing caches to try to gain extra memory");
|
|
||||||
Scheduler::signal_oom_thread();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (free_mem * 8 == total())
|
|
||||||
{
|
|
||||||
kwarnln("Only 1/8 of memory is free, clearing caches to try to gain extra memory");
|
|
||||||
Scheduler::signal_oom_thread();
|
|
||||||
}
|
|
||||||
|
|
||||||
return index * ARCH_PAGE_SIZE;
|
return index * ARCH_PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +65,6 @@ class Socket : public VFS::FileInode
|
|||||||
m_metadata.nlinks--;
|
m_metadata.nlinks--;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool can_accept_connections() const = 0;
|
|
||||||
|
|
||||||
virtual bool can_read_data() const = 0;
|
|
||||||
|
|
||||||
virtual bool peer_disconnected() const = 0;
|
|
||||||
|
|
||||||
virtual ~Socket() = default;
|
virtual ~Socket() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
|
|
||||||
UnixSocket::UnixSocket()
|
UnixSocket::UnixSocket()
|
||||||
{
|
{
|
||||||
m_metadata.initialize_times();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnixSocket::UnixSocket(UnixSocket* peer) : m_state(State::Connected), m_peer(peer)
|
UnixSocket::UnixSocket(UnixSocket* peer) : m_state(State::Connected), m_peer(peer)
|
||||||
{
|
{
|
||||||
m_metadata.initialize_times();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnixSocket::~UnixSocket()
|
UnixSocket::~UnixSocket()
|
||||||
@ -43,8 +41,6 @@ Result<usize> UnixSocket::send(const u8* buf, usize length, int)
|
|||||||
|
|
||||||
TRY(m_peer->m_data.append_data(buf, length));
|
TRY(m_peer->m_data.append_data(buf, length));
|
||||||
|
|
||||||
m_peer->m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,14 +52,14 @@ Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
|
|||||||
return m_data.dequeue_data(buf, length);
|
return m_data.dequeue_data(buf, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
|
||||||
SharedPtr<VFS::Inode> working_directory, SharedPtr<UnixSocket> socket)
|
SharedPtr<UnixSocket> socket)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -95,8 +91,7 @@ Result<void> UnixSocket::bind(struct sockaddr* addr, socklen_t addrlen)
|
|||||||
m_metadata.uid = current->auth.euid;
|
m_metadata.uid = current->auth.euid;
|
||||||
m_metadata.gid = current->auth.egid;
|
m_metadata.gid = current->auth.egid;
|
||||||
|
|
||||||
auto rc = bind_socket_to_fs(path.chars(), current->auth, ¤t->extra_groups, current->current_directory,
|
auto rc = bind_socket_to_fs(path.chars(), current->auth, current->current_directory, SharedPtr<Socket> { this });
|
||||||
SharedPtr<Socket> { this });
|
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
if (rc.error() == EEXIST) return err(EADDRINUSE);
|
if (rc.error() == EEXIST) return err(EADDRINUSE);
|
||||||
@ -128,11 +123,10 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
|||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
if (inode->type() != VFS::InodeType::Socket)
|
if (inode->type() != VFS::InodeType::Socket)
|
||||||
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto socket = (SharedPtr<UnixSocket>)inode;
|
auto socket = (SharedPtr<UnixSocket>)inode;
|
||||||
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
||||||
@ -213,7 +207,5 @@ Result<SharedPtr<OpenFileDescription>> UnixSocket::accept(Registers* regs, int f
|
|||||||
*addr = (struct sockaddr*)&peer->m_addr;
|
*addr = (struct sockaddr*)&peer->m_addr;
|
||||||
*addrlen = peer->m_addrlen;
|
*addrlen = peer->m_addrlen;
|
||||||
|
|
||||||
m_metadata.update_mtime();
|
|
||||||
|
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,6 @@ class UnixSocket : public Socket
|
|||||||
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool can_read_data() const override
|
|
||||||
{
|
|
||||||
return (m_state == Connected || m_state == Reset) && m_data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool can_accept_connections() const override
|
|
||||||
{
|
|
||||||
return !m_listen_queue.is_empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool peer_disconnected() const override
|
|
||||||
{
|
|
||||||
return m_state == Reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> send(const u8*, usize, int) override;
|
Result<usize> send(const u8*, usize, int) override;
|
||||||
Result<usize> recv(u8*, usize, int) const override;
|
Result<usize> recv(u8*, usize, int) const override;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -14,10 +14,10 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (PathParser::is_absolute(path.view()))
|
if (PathParser::is_absolute(path.view()))
|
||||||
{
|
{
|
||||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups));
|
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
inode->add_handle();
|
inode->add_handle();
|
||||||
if (current->current_directory) current->current_directory->remove_handle();
|
if (current->current_directory) current->current_directory->remove_handle();
|
||||||
@ -29,11 +29,10 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SharedPtr<VFS::Inode> inode =
|
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto old_wdir = current->current_directory_path.view();
|
auto old_wdir = current->current_directory_path.view();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "Pledge.h"
|
#include "Pledge.h"
|
||||||
|
#include "arch/Timer.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Clock.h"
|
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include <bits/clockid.h>
|
#include <bits/clockid.h>
|
||||||
#include <bits/timespec.h>
|
#include <bits/timespec.h>
|
||||||
@ -15,24 +15,18 @@ Result<u64> sys_clock_gettime(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
Clock* clock;
|
|
||||||
|
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case CLOCK_MONOTONIC: {
|
case CLOCK_MONOTONIC: {
|
||||||
clock = &g_monotonic_clock;
|
if (!MemoryManager::copy_to_user_typed(ts, Timer::monotonic_clock())) return err(EFAULT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CLOCK_REALTIME: {
|
case CLOCK_REALTIME: {
|
||||||
clock = &g_realtime_clock;
|
if (!MemoryManager::copy_to_user_typed(ts, Timer::realtime_clock())) return err(EFAULT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: return err(EINVAL);
|
default: return err(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec time;
|
|
||||||
clock->get_time(time);
|
|
||||||
if (!MemoryManager::copy_to_user_typed(ts, &time)) return err(EFAULT);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -68,10 +68,9 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_exec));
|
TRY(check_pledge(current, Promise::p_exec));
|
||||||
|
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
#ifdef EXEC_DEBUG
|
#ifdef EXEC_DEBUG
|
||||||
kdbgln("exec: attempting to replace current image with %s", path.chars());
|
kdbgln("exec: attempting to replace current image with %s", path.chars());
|
||||||
@ -108,18 +107,6 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
|
||||||
current->real_timer.disarm();
|
|
||||||
current->virtual_timer.disarm();
|
|
||||||
current->profiling_timer.disarm();
|
|
||||||
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
|
|
||||||
{
|
|
||||||
if (current->posix_timers[i].has_value())
|
|
||||||
{
|
|
||||||
current->posix_timers[i]->disarm();
|
|
||||||
current->posix_timers[i] = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < FD_MAX; i++)
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
{
|
{
|
||||||
auto& descriptor = current->fd_table[i];
|
auto& descriptor = current->fd_table[i];
|
||||||
@ -161,8 +148,6 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
|||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
|
|
||||||
auto extra_groups = TRY(current->extra_groups.shallow_copy());
|
|
||||||
|
|
||||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
||||||
|
|
||||||
memcpy(¤t->regs, regs, sizeof(*regs));
|
memcpy(¤t->regs, regs, sizeof(*regs));
|
||||||
@ -184,13 +169,6 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
|||||||
thread->parent = current;
|
thread->parent = current;
|
||||||
thread->promises = current->promises;
|
thread->promises = current->promises;
|
||||||
thread->execpromises = current->execpromises;
|
thread->execpromises = current->execpromises;
|
||||||
thread->controlling_terminal = current->controlling_terminal;
|
|
||||||
thread->pgid = current->pgid;
|
|
||||||
thread->sid = current->sid;
|
|
||||||
thread->extra_groups = move(extra_groups);
|
|
||||||
|
|
||||||
thread->virtual_clock.set_resolution(1'000'000);
|
|
||||||
thread->profiling_clock.set_resolution(1'000'000);
|
|
||||||
|
|
||||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
||||||
|
|
||||||
|
@ -259,10 +259,9 @@ Result<u64> sys_truncate(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_wpath));
|
TRY(check_pledge(current, Promise::p_wpath));
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
TRY(inode->truncate(length));
|
TRY(inode->truncate(length));
|
||||||
|
|
||||||
@ -309,8 +308,8 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (allow_write_access)
|
if (allow_write_access)
|
||||||
{
|
{
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups) &&
|
if (!VFS::can_write(inode, current->auth) && current->auth.euid != inode->metadata().uid &&
|
||||||
current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
|
current->auth.euid != 0)
|
||||||
return err(EACCES);
|
return err(EACCES);
|
||||||
}
|
}
|
||||||
else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
|
else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
|
||||||
@ -319,7 +318,7 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
|
|||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
if (ktimes[0].tv_nsec != UTIME_OMIT)
|
if (ktimes[0].tv_nsec != UTIME_OMIT)
|
||||||
{
|
{
|
||||||
if (ktimes[0].tv_nsec == UTIME_NOW) g_realtime_clock.get_time(metadata.atime);
|
if (ktimes[0].tv_nsec == UTIME_NOW) metadata.atime = *Timer::realtime_clock();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ktimes[0].tv_nsec < 0 || ktimes[0].tv_nsec > 999'999'999) return err(EINVAL);
|
if (ktimes[0].tv_nsec < 0 || ktimes[0].tv_nsec > 999'999'999) return err(EINVAL);
|
||||||
@ -328,7 +327,7 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
|
|||||||
}
|
}
|
||||||
if (ktimes[1].tv_nsec != UTIME_OMIT)
|
if (ktimes[1].tv_nsec != UTIME_OMIT)
|
||||||
{
|
{
|
||||||
if (ktimes[1].tv_nsec == UTIME_NOW) g_realtime_clock.get_time(metadata.mtime);
|
if (ktimes[1].tv_nsec == UTIME_NOW) metadata.mtime = *Timer::realtime_clock();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ktimes[1].tv_nsec < 0 || ktimes[1].tv_nsec > 999'999'999) return err(EINVAL);
|
if (ktimes[1].tv_nsec < 0 || ktimes[1].tv_nsec > 999'999'999) return err(EINVAL);
|
||||||
|
@ -130,21 +130,18 @@ Result<u64> sys_setpgid(Registers*, SyscallArgs args)
|
|||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
if (thread != current && thread->parent != current) return err(ESRCH);
|
if (thread != current && thread->parent != current) return err(ESRCH);
|
||||||
|
|
||||||
if (thread->is_session_leader() || thread->sid != current->sid) return err(EPERM);
|
// FIXME: Weird session stuff, we don't have that currently.
|
||||||
|
|
||||||
if (thread->has_called_exec) return err(EPERM);
|
if (thread->has_called_exec) return err(EPERM);
|
||||||
|
|
||||||
if (pgid != current->id)
|
if (pgid != current->id)
|
||||||
{
|
{
|
||||||
bool pgid_exists = false;
|
bool pgid_exists = false;
|
||||||
pid_t sid;
|
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
|
||||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* t) {
|
|
||||||
pgid_exists = true;
|
pgid_exists = true;
|
||||||
sid = t->sid; // this should be the same for all threads in the process group
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (!pgid_exists) return err(EPERM);
|
if (!pgid_exists) return err(EPERM);
|
||||||
if (sid != thread->sid) return err(EPERM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->pgid = (u64)pgid;
|
thread->pgid = (u64)pgid;
|
||||||
@ -167,33 +164,6 @@ Result<u64> sys_getpgid(Registers*, SyscallArgs args)
|
|||||||
return (u64)thread->pgid;
|
return (u64)thread->pgid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_setsid(Registers*, SyscallArgs)
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
|
||||||
|
|
||||||
if (current->pgid == current->id) return err(EPERM);
|
|
||||||
|
|
||||||
current->sid = current->pgid = current->id;
|
|
||||||
current->controlling_terminal = {};
|
|
||||||
|
|
||||||
return current->sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_getsid(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
pid_t pid = (pid_t)args[0];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
if (pid == 0) pid = current->id;
|
|
||||||
|
|
||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
|
||||||
|
|
||||||
return thread->sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
int dirfd = (int)args[0];
|
int dirfd = (int)args[0];
|
||||||
@ -237,54 +207,3 @@ Result<u64> sys_fchownat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getgroups(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
int ngroups = (int)args[0];
|
|
||||||
gid_t* grouplist = (gid_t*)args[1];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
if (!ngroups) return current->extra_groups.size();
|
|
||||||
if (ngroups < 0) return err(EINVAL);
|
|
||||||
|
|
||||||
if (static_cast<usize>(ngroups) < current->extra_groups.size()) return err(EINVAL);
|
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user(grouplist, current->extra_groups.data(),
|
|
||||||
current->extra_groups.size() * sizeof(gid_t)))
|
|
||||||
return err(EFAULT);
|
|
||||||
|
|
||||||
return current->extra_groups.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_setgroups(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
int ngroups = (int)args[0];
|
|
||||||
const gid_t* grouplist = (const gid_t*)args[1];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
|
||||||
|
|
||||||
Credentials& auth = current->auth;
|
|
||||||
if (auth.euid != 0) return err(EPERM);
|
|
||||||
|
|
||||||
if (!ngroups)
|
|
||||||
{
|
|
||||||
current->extra_groups.clear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngroups < 0 || ngroups > 32) return err(EINVAL);
|
|
||||||
|
|
||||||
TRY(current->extra_groups.try_reserve(ngroups));
|
|
||||||
|
|
||||||
current->extra_groups.mutate([&](gid_t* list, usize) -> usize {
|
|
||||||
if (MemoryManager::copy_from_user(grouplist, list, ngroups * sizeof(gid_t))) return ngroups;
|
|
||||||
return current->extra_groups.size();
|
|
||||||
});
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
@ -23,7 +23,7 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
|
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
|
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child = TRY(inode->find(basename.chars()));
|
auto child = TRY(inode->find(basename.chars()));
|
||||||
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -52,7 +52,7 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (target->fs() != parent_inode->fs()) return err(EXDEV);
|
if (target->fs() != parent_inode->fs()) return err(EXDEV);
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(newpath.view()));
|
auto child_name = TRY(PathParser::basename(newpath.view()));
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ Result<u64> sys_mkdir(Registers*, SyscallArgs args)
|
|||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_cpath));
|
TRY(check_pledge(current, Promise::p_cpath));
|
||||||
|
|
||||||
auto inode = TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, ¤t->extra_groups,
|
auto inode =
|
||||||
current->current_directory));
|
TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, current->current_directory));
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
metadata.uid = current->auth.euid;
|
||||||
metadata.gid = current->auth.egid;
|
metadata.gid = current->auth.egid;
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Common.h>
|
#include <luna/Common.h>
|
||||||
|
|
||||||
|
constexpr uintptr_t USERSPACE_HEAP_BASE = 0x3000000;
|
||||||
|
|
||||||
Result<u64> sys_mmap(Registers*, SyscallArgs args)
|
Result<u64> sys_mmap(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
mmap_params params;
|
mmap_params params;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "Pledge.h"
|
#include "Pledge.h"
|
||||||
#include "fs/FSRegistry.h"
|
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "fs/devpts/FileSystem.h"
|
|
||||||
#include "fs/ext2/FileSystem.h"
|
#include "fs/ext2/FileSystem.h"
|
||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
@ -19,8 +17,7 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
|
|||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->auth.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
|
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(source.chars(), current->auth, current->current_directory));
|
||||||
TRY(VFS::resolve_path(source.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
|
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
|
||||||
dev_t device_id = inode->metadata().devid;
|
dev_t device_id = inode->metadata().devid;
|
||||||
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
|
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
|
||||||
@ -28,20 +25,18 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
SharedPtr<VFS::FileSystem> fs;
|
SharedPtr<VFS::FileSystem> fs;
|
||||||
|
|
||||||
auto maybe_virtual = FSRegistry::lookup_virtual_filesystem(fstype.view());
|
if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create());
|
||||||
if (maybe_virtual.has_value())
|
else if (fstype.view() == "devfs")
|
||||||
|
fs = TRY(DeviceRegistry::create_devfs_instance());
|
||||||
|
else if (fstype.view() == "ext2")
|
||||||
{
|
{
|
||||||
auto factory = maybe_virtual.release_value();
|
auto source_device = TRY(get_source());
|
||||||
fs = TRY(factory());
|
fs = TRY(Ext2::FileSystem::create(source_device));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
return err(ENODEV);
|
||||||
auto factory = TRY(FSRegistry::lookup_physical_filesystem(fstype.view()));
|
|
||||||
auto device = TRY(get_source());
|
|
||||||
fs = TRY(factory(device));
|
|
||||||
}
|
|
||||||
|
|
||||||
TRY(VFS::mount(target.chars(), fs, current->auth, ¤t->extra_groups, current->current_directory));
|
TRY(VFS::mount(target.chars(), fs, current->auth, current->current_directory));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -54,7 +49,7 @@ Result<u64> sys_umount(Registers*, SyscallArgs args)
|
|||||||
TRY(check_pledge(current, Promise::p_mount));
|
TRY(check_pledge(current, Promise::p_mount));
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->auth.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
TRY(VFS::umount(target.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
TRY(VFS::umount(target.chars(), current->auth, current->current_directory));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,7 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
|
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
|
||||||
{
|
{
|
||||||
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, ¤t->extra_groups,
|
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, parent_inode));
|
||||||
parent_inode));
|
|
||||||
// FIXME: Pass these in create_file().
|
// FIXME: Pass these in create_file().
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
metadata.uid = current->auth.euid;
|
||||||
@ -59,12 +58,10 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
return err(EEXIST);
|
return err(EEXIST);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth)) return err(EACCES);
|
||||||
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
}
|
}
|
||||||
|
|
||||||
inode = TRY(inode->open());
|
|
||||||
|
|
||||||
// This should only be possible if O_NOFOLLOW was in flags.
|
// This should only be possible if O_NOFOLLOW was in flags.
|
||||||
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
|
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "Pledge.h"
|
#include "Pledge.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "net/Socket.h"
|
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include <bits/poll.h>
|
#include <bits/poll.h>
|
||||||
@ -48,31 +47,11 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
|
|||||||
auto& inode = inodes[i];
|
auto& inode = inodes[i];
|
||||||
if (!inode) continue;
|
if (!inode) continue;
|
||||||
|
|
||||||
if (kfds[i].events & POLLIN)
|
if (kfds[i].events & POLLIN && !inode->will_block_if_read())
|
||||||
{
|
|
||||||
if (inode->type() == VFS::InodeType::Socket)
|
|
||||||
{
|
|
||||||
auto socket = (Socket*)inode.ptr();
|
|
||||||
if (socket->can_read_data() || socket->can_accept_connections())
|
|
||||||
{
|
{
|
||||||
fds_with_events++;
|
fds_with_events++;
|
||||||
kfds[i].revents |= POLLIN;
|
kfds[i].revents |= POLLIN;
|
||||||
}
|
}
|
||||||
if (socket->peer_disconnected())
|
|
||||||
{
|
|
||||||
fds_with_events++;
|
|
||||||
kfds[i].revents |= POLLHUP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!inode->will_block_if_read())
|
|
||||||
{
|
|
||||||
fds_with_events++;
|
|
||||||
kfds[i].revents |= POLLIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fds_with_events && (timeout > 0 || infinite))
|
if (!fds_with_events && (timeout > 0 || infinite))
|
||||||
|
@ -1,240 +0,0 @@
|
|||||||
#define _TIMESPEC_MACROS_INCLUDED
|
|
||||||
#include "Pledge.h"
|
|
||||||
#include "memory/MemoryManager.h"
|
|
||||||
#include "sys/Syscall.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include "thread/Timer.h"
|
|
||||||
#include <bits/clockid.h>
|
|
||||||
#include <bits/itimer.h>
|
|
||||||
#include <bits/sigevent.h>
|
|
||||||
#include <luna/Common.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
int which = (int)args[0];
|
|
||||||
const struct itimerval* new_timer = (const struct itimerval*)args[1];
|
|
||||||
struct itimerval* old_timer = (struct itimerval*)args[2];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
Timer* timer;
|
|
||||||
Clock* clock;
|
|
||||||
switch (which)
|
|
||||||
{
|
|
||||||
case ITIMER_REAL:
|
|
||||||
timer = ¤t->real_timer;
|
|
||||||
clock = &g_realtime_clock;
|
|
||||||
break;
|
|
||||||
case ITIMER_VIRTUAL:
|
|
||||||
timer = ¤t->virtual_timer;
|
|
||||||
clock = ¤t->virtual_clock;
|
|
||||||
break;
|
|
||||||
case ITIMER_PROF:
|
|
||||||
timer = ¤t->profiling_timer;
|
|
||||||
clock = ¤t->profiling_clock;
|
|
||||||
break;
|
|
||||||
default: return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_timer)
|
|
||||||
{
|
|
||||||
if (timer->active_clock)
|
|
||||||
{
|
|
||||||
struct itimerval result;
|
|
||||||
auto total = timer->active_clock->from_ticks(timer->interval_ticks);
|
|
||||||
auto left = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
|
|
||||||
result.it_interval = TIMESPEC_TO_TIMEVAL(total);
|
|
||||||
result.it_value = TIMESPEC_TO_TIMEVAL(left);
|
|
||||||
if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct itimerval result;
|
|
||||||
memset(&result, 0, sizeof(result));
|
|
||||||
if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_timer)
|
|
||||||
{
|
|
||||||
timer->disarm();
|
|
||||||
|
|
||||||
struct itimerval itimer;
|
|
||||||
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
|
|
||||||
|
|
||||||
timer->signo = SIGALRM; // FIXME: Also use SIGVTALRM or SIGPROF for other timer types.
|
|
||||||
timer->thread = current;
|
|
||||||
|
|
||||||
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
|
|
||||||
{
|
|
||||||
timer->restart = true;
|
|
||||||
timer->interval_ticks =
|
|
||||||
(itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
timer->restart = false;
|
|
||||||
|
|
||||||
if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0)
|
|
||||||
{
|
|
||||||
u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution();
|
|
||||||
timer->arm(clock, ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_timer_create(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
clockid_t clockid = (clockid_t)args[0];
|
|
||||||
struct sigevent* sevp = (struct sigevent*)args[1];
|
|
||||||
timer_t* timerid = (timer_t*)args[2];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
struct sigevent ksevp;
|
|
||||||
if (!sevp)
|
|
||||||
{
|
|
||||||
ksevp.sigev_notify = SIGEV_SIGNAL;
|
|
||||||
ksevp.sigev_signo = SIGALRM;
|
|
||||||
}
|
|
||||||
else if (!MemoryManager::copy_from_user_typed(sevp, &ksevp))
|
|
||||||
return err(EFAULT);
|
|
||||||
|
|
||||||
Clock* clock;
|
|
||||||
switch (clockid)
|
|
||||||
{
|
|
||||||
case CLOCK_REALTIME: clock = &g_realtime_clock; break;
|
|
||||||
case CLOCK_MONOTONIC: clock = &g_monotonic_clock; break;
|
|
||||||
default: return err(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksevp.sigev_notify != SIGEV_SIGNAL) return err(ENOTSUP);
|
|
||||||
if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL);
|
|
||||||
|
|
||||||
int id = TRY(current->allocate_timerid());
|
|
||||||
current->posix_timers[id] = Timer {};
|
|
||||||
|
|
||||||
Timer* timer = current->posix_timers[id].value_ptr();
|
|
||||||
timer->signo = ksevp.sigev_signo;
|
|
||||||
timer->designated_clock = clock;
|
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user_typed(timerid, &id)) return err(EFAULT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_timer_settime(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
timer_t timerid = (timer_t)args[0];
|
|
||||||
int flags = (int)args[1];
|
|
||||||
const struct itimerspec* new_value = (const struct itimerspec*)args[2];
|
|
||||||
struct itimerspec* old_value = (struct itimerspec*)args[3];
|
|
||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
|
||||||
if (flags > 0) return err(ENOTSUP);
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
|
|
||||||
Timer* timer = current->posix_timers[timerid].value_ptr();
|
|
||||||
|
|
||||||
if (old_value)
|
|
||||||
{
|
|
||||||
if (timer->active_clock)
|
|
||||||
{
|
|
||||||
struct itimerspec result;
|
|
||||||
result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks);
|
|
||||||
result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
|
|
||||||
if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct itimerspec result;
|
|
||||||
memset(&result, 0, sizeof(result));
|
|
||||||
if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct itimerspec itimer;
|
|
||||||
if (!MemoryManager::copy_from_user_typed(new_value, &itimer)) return err(EFAULT);
|
|
||||||
|
|
||||||
timer->disarm();
|
|
||||||
|
|
||||||
Clock* clock = timer->designated_clock;
|
|
||||||
check(clock);
|
|
||||||
|
|
||||||
timer->thread = current;
|
|
||||||
|
|
||||||
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0)
|
|
||||||
{
|
|
||||||
timer->restart = true;
|
|
||||||
timer->interval_ticks =
|
|
||||||
(itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_nsec) / clock->resolution();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
timer->restart = false;
|
|
||||||
|
|
||||||
if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_nsec != 0)
|
|
||||||
{
|
|
||||||
u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_nsec) / clock->resolution();
|
|
||||||
timer->arm(clock, ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_timer_gettime(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
timer_t timerid = (timer_t)args[0];
|
|
||||||
struct itimerspec* value = (struct itimerspec*)args[1];
|
|
||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
|
|
||||||
Timer* timer = current->posix_timers[timerid].value_ptr();
|
|
||||||
|
|
||||||
if (timer->active_clock)
|
|
||||||
{
|
|
||||||
struct itimerspec result;
|
|
||||||
result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks);
|
|
||||||
result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
|
|
||||||
if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct itimerspec result;
|
|
||||||
memset(&result, 0, sizeof(result));
|
|
||||||
if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_timer_delete(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
timer_t timerid = (timer_t)args[0];
|
|
||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
|
||||||
|
|
||||||
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
|
|
||||||
|
|
||||||
Timer* timer = current->posix_timers[timerid].value_ptr();
|
|
||||||
timer->disarm();
|
|
||||||
|
|
||||||
current->posix_timers[timerid] = {};
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -54,66 +54,17 @@ Result<u64> sys_kill(Registers*, SyscallArgs args)
|
|||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
int signo = (int)args[1];
|
int signo = (int)args[1];
|
||||||
|
|
||||||
auto send_signal = [&](Thread* target) -> Result<void> {
|
// FIXME: Support this case.
|
||||||
if (current->auth.euid != 0 && current->auth.euid != target->auth.euid &&
|
if (pid <= 0) return err(ENOTSUP);
|
||||||
current->auth.egid != target->auth.egid)
|
|
||||||
|
auto* target = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
if (current->auth.euid != 0 && current->auth.euid != target->auth.euid && current->auth.egid != target->auth.egid)
|
||||||
return err(EPERM);
|
return err(EPERM);
|
||||||
if (target->is_kernel) return {};
|
if (target->is_kernel) return 0;
|
||||||
if (signo == 0) return {};
|
if (signo == 0) return 0;
|
||||||
|
|
||||||
target->send_signal(signo);
|
target->send_signal(signo);
|
||||||
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pid > 0)
|
|
||||||
{
|
|
||||||
auto* target = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
|
||||||
TRY(send_signal(target));
|
|
||||||
}
|
|
||||||
else if (pid == 0)
|
|
||||||
{
|
|
||||||
int errno = -1;
|
|
||||||
bool pgid_exists = false;
|
|
||||||
Scheduler::for_each_in_process_group(current->pgid, [&](Thread* target) {
|
|
||||||
pgid_exists = true;
|
|
||||||
auto rc = send_signal(target);
|
|
||||||
if (rc.has_error())
|
|
||||||
{
|
|
||||||
errno = rc.error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (errno > 0) return err(errno);
|
|
||||||
if (!pgid_exists) return err(ESRCH);
|
|
||||||
}
|
|
||||||
else if (pid == -1)
|
|
||||||
{
|
|
||||||
for (auto* thread : g_threads)
|
|
||||||
{
|
|
||||||
// We ignore permission errors here.
|
|
||||||
if (thread != current && thread->id != 1) send_signal(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pid < -1)
|
|
||||||
{
|
|
||||||
int errno = -1;
|
|
||||||
bool pgid_exists = false;
|
|
||||||
Scheduler::for_each_in_process_group(-pid, [&](Thread* target) {
|
|
||||||
pgid_exists = true;
|
|
||||||
auto rc = send_signal(target);
|
|
||||||
if (rc.has_error())
|
|
||||||
{
|
|
||||||
errno = rc.error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (errno > 0) return err(errno);
|
|
||||||
if (!pgid_exists) return err(ESRCH);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,49 +99,3 @@ Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_pause(Registers* regs, SyscallArgs)
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
kernel_wait_for_event();
|
|
||||||
if (current->interrupted)
|
|
||||||
{
|
|
||||||
if (current->will_ignore_pending_signal())
|
|
||||||
{
|
|
||||||
current->process_pending_signals(regs);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return err(EINTR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_sigsuspend(Registers* regs, SyscallArgs args)
|
|
||||||
{
|
|
||||||
const sigset_t* set = (const sigset_t*)args[0];
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
sigset_t kset;
|
|
||||||
if (!MemoryManager::copy_from_user_typed(set, &kset)) return err(EFAULT);
|
|
||||||
|
|
||||||
sigset_t oldset = current->signal_mask.value();
|
|
||||||
current->signal_mask = kset;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
kernel_wait_for_event();
|
|
||||||
if (current->interrupted)
|
|
||||||
{
|
|
||||||
if (current->will_ignore_pending_signal())
|
|
||||||
{
|
|
||||||
current->process_pending_signals(regs);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
current->signal_mask = oldset;
|
|
||||||
return err(EINTR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -81,9 +81,9 @@ Result<u64> sys_faccessat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
|
||||||
|
|
||||||
if ((amode & R_OK) && !VFS::can_read(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
if ((amode & R_OK) && !VFS::can_read(inode, creds)) return err(EACCES);
|
||||||
if ((amode & W_OK) && !VFS::can_write(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
if ((amode & W_OK) && !VFS::can_write(inode, creds)) return err(EACCES);
|
||||||
if ((amode & X_OK) && !VFS::can_execute(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
if ((amode & X_OK) && !VFS::can_execute(inode, creds)) return err(EACCES);
|
||||||
|
|
||||||
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
|
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
|
||||||
// otherwise.
|
// otherwise.
|
||||||
|