Compare commits
147 Commits
b8ca749a6c
...
30e4ef970e
Author | SHA1 | Date | |
---|---|---|---|
30e4ef970e | |||
d572d56460 | |||
18115ca04a | |||
8d90d146b2 | |||
88d1da59e8 | |||
bd8aaa917f | |||
a98e9e8009 | |||
b0506bf88f | |||
d89a823924 | |||
cba0a23db9 | |||
7cdb967730 | |||
62e14e7580 | |||
38fae0c97b | |||
4dcee8f828 | |||
84c1871d30 | |||
3a51807fa6 | |||
9cdfdbc6f9 | |||
1094042a7d | |||
1a6ad11462 | |||
b61307e5cb | |||
04d074538f | |||
2c798b8bf0 | |||
597aada09e | |||
4ec1af5e51 | |||
7a8ef52408 | |||
cb205c851c | |||
67a9d130e2 | |||
b75bd4cd14 | |||
9bb5371e8c | |||
47d505dcbb | |||
5117b410db | |||
6de90b3c93 | |||
01111442d3 | |||
1fa8aeecce | |||
30d1dad149 | |||
1db60b5a82 | |||
a7c6163e7c | |||
dc169124cc | |||
3db0c4fed2 | |||
1506331872 | |||
0dbfbe6395 | |||
84bed3ceb3 | |||
d7c563aebd | |||
3a73d49aa1 | |||
8f6bd29da3 | |||
1f4c4928cc | |||
29174ca228 | |||
e7d482e78a | |||
4d106b6b8c | |||
65d3195caa | |||
dc7e503342 | |||
e481ce45d0 | |||
e098a3269a | |||
38541e22e9 | |||
5911b052dc | |||
a5ad8e16de | |||
300d68088b | |||
64d0f30bd9 | |||
1035b91a3d | |||
a935039e78 | |||
18130847c1 | |||
411c6c40cd | |||
4a3a92e9d4 | |||
4a764bc315 | |||
e203772488 | |||
efc6d03f23 | |||
1a2fce5316 | |||
4753e80583 | |||
35b1b36599 | |||
916a73ca95 | |||
9bab4c62a1 | |||
72dadc6706 | |||
b25e212880 | |||
88180b34bd | |||
b08ebdc3cb | |||
cd86d1d6d0 | |||
b742a08cbe | |||
77560bbc3e | |||
d7fee26aa2 | |||
9184bbfef6 | |||
349ba0acb1 | |||
d9d8f7cdc7 | |||
293a992133 | |||
4a7f68b989 | |||
3a1c22bb93 | |||
3ed9e578c7 | |||
96f3d29d37 | |||
42eb0a1d74 | |||
4616997f5b | |||
58eb2d7703 | |||
44e4ca804a | |||
3eb78aa5f3 | |||
abaf24d0da | |||
1215c38d75 | |||
a0b45a51de | |||
d9b7e8edc0 | |||
3628464284 | |||
d1801d484c | |||
dcd93cce3a | |||
6beea7f817 | |||
0fad179485 | |||
052ae4902e | |||
7058ec945a | |||
6982a8c07e | |||
1444cbb3df | |||
1d6a39c924 | |||
376247ba8a | |||
53ec448e33 | |||
2572d5b238 | |||
458e0a87cc | |||
48df90e636 | |||
a2303665fc | |||
df590f4e26 | |||
7fbc644753 | |||
967758d464 | |||
ac4bbd135b | |||
6c5d6aaf00 | |||
3e277b5d6f | |||
8adfb6fdb9 | |||
ae7c841fff | |||
b34f2149ee | |||
c5a867d81c | |||
15dcd6ad15 | |||
b4a5aff071 | |||
80914f0bb9 | |||
0c1d33f2ec | |||
52ce4b28aa | |||
7b4cfd52cd | |||
bcdec62f51 | |||
f3ffdd32d4 | |||
77dcfab5ef | |||
b1e400d795 | |||
f0fc3ec7ca | |||
7c8f195088 | |||
e1ac9473a2 | |||
fb22e14524 | |||
9d33e22ae0 | |||
b48d1024a8 | |||
24f4ce9669 | |||
099f6131d1 | |||
36bc217056 | |||
cb28e2a385 | |||
fb09eb97ce | |||
5b69ce554c | |||
97e9fceaa4 | |||
188a97cf54 | |||
89fd57dea4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ env-local.sh
|
|||||||
initrd/bin/**
|
initrd/bin/**
|
||||||
initrd/tests/**
|
initrd/tests/**
|
||||||
base/
|
base/
|
||||||
|
.fakeroot
|
||||||
|
@ -5,7 +5,7 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
|||||||
|
|
||||||
set(CMAKE_CROSSCOMPILING true)
|
set(CMAKE_CROSSCOMPILING true)
|
||||||
|
|
||||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.1.0)
|
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.2.0)
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Luna
|
# Luna
|
||||||
A very basic POSIX operating system for desktop computers, written mostly in C++ and C. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
|
A very basic POSIX-based operating system for personal computers, written in C++. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
|
||||||
|
|
||||||
## Another UNIX clone?
|
## Another UNIX clone?
|
||||||
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
|
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
|
||||||
@ -8,7 +8,7 @@ A very basic POSIX operating system for desktop computers, written mostly in C++
|
|||||||
- x86_64-compatible lightweight [kernel](kernel/).
|
- x86_64-compatible lightweight [kernel](kernel/).
|
||||||
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||||
- [Virtual file system](kernel/src/fs/) with a simple but working [tmpfs](kernel/src/fs/tmpfs/) populated from the initial ramdisk.
|
- [Virtual file system](kernel/src/fs/) with a simple but working [tmpfs](kernel/src/fs/tmpfs/) populated from the initial ramdisk.
|
||||||
- Can [load ELF programs](kernel/src/ELF.cpp) from the file system as userspace tasks.
|
- Can [load ELF programs](kernel/src/thread/ELF.cpp) from the file system as userspace tasks.
|
||||||
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be mostly POSIX-compatible.
|
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be mostly POSIX-compatible.
|
||||||
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
||||||
- Fully [UTF-8 aware](libluna/include/luna/Utf8.h), **everywhere**.
|
- Fully [UTF-8 aware](libluna/include/luna/Utf8.h), **everywhere**.
|
||||||
@ -76,7 +76,7 @@ These images do reflect the latest changes on the `main` branch, but are obvious
|
|||||||
|
|
||||||
## Is there third-party software I can use on Luna?
|
## Is there third-party software I can use on Luna?
|
||||||
|
|
||||||
Not right now, but hopefully we can start porting some software soon! (After the VFS and fork/exec are done, of course. So, in a long time.)
|
Not right now, but hopefully we can start porting some software soon!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Luna is open-source and free software under the [BSD-2 License](LICENSE).
|
Luna is open-source and free software under the [BSD-2 License](LICENSE).
|
||||||
|
@ -1,27 +1,31 @@
|
|||||||
function(luna_app SOURCE_FILE APP_NAME SETUID)
|
function(luna_app SOURCE_FILE APP_NAME)
|
||||||
add_executable(${APP_NAME} ${SOURCE_FILE})
|
add_executable(${APP_NAME} ${SOURCE_FILE})
|
||||||
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||||
add_dependencies(${APP_NAME} libc)
|
add_dependencies(${APP_NAME} libc)
|
||||||
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
|
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
|
||||||
target_link_libraries(${APP_NAME} PRIVATE os)
|
target_link_libraries(${APP_NAME} PRIVATE os)
|
||||||
if(${SETUID})
|
|
||||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID)
|
|
||||||
else()
|
|
||||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin)
|
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin)
|
||||||
endif()
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
luna_app(init.cpp init OFF)
|
luna_app(init.cpp init)
|
||||||
luna_app(env.cpp env OFF)
|
luna_app(env.cpp env)
|
||||||
luna_app(su.cpp su ON)
|
luna_app(su.cpp su)
|
||||||
luna_app(sh.cpp sh OFF)
|
luna_app(sh.cpp sh)
|
||||||
luna_app(cat.cpp cat OFF)
|
luna_app(cat.cpp cat)
|
||||||
luna_app(date.cpp date OFF)
|
luna_app(date.cpp date)
|
||||||
luna_app(edit.cpp edit OFF)
|
luna_app(edit.cpp edit)
|
||||||
luna_app(ls.cpp ls OFF)
|
luna_app(ls.cpp ls)
|
||||||
luna_app(chown.cpp chown OFF)
|
luna_app(chown.cpp chown)
|
||||||
luna_app(chmod.cpp chmod OFF)
|
luna_app(chmod.cpp chmod)
|
||||||
luna_app(mkdir.cpp mkdir OFF)
|
luna_app(mkdir.cpp mkdir)
|
||||||
luna_app(rm.cpp rm OFF)
|
luna_app(rm.cpp rm)
|
||||||
luna_app(stat.cpp stat OFF)
|
luna_app(stat.cpp stat)
|
||||||
luna_app(uname.cpp uname OFF)
|
luna_app(uname.cpp uname)
|
||||||
|
luna_app(base64.cpp base64)
|
||||||
|
luna_app(login.cpp login)
|
||||||
|
luna_app(ipc-test.cpp ipc-test)
|
||||||
|
luna_app(mount.cpp mount)
|
||||||
|
luna_app(umount.cpp umount)
|
||||||
|
luna_app(ps.cpp ps)
|
||||||
|
luna_app(time.cpp time)
|
||||||
|
luna_app(ln.cpp ln)
|
||||||
|
39
apps/base64.cpp
Normal file
39
apps/base64.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <luna/Base64.h>
|
||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
StringView path;
|
||||||
|
bool decode { false };
|
||||||
|
bool allow_garbage { false };
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Encode or decode Base64 data. If not given a file, reads from standard input.");
|
||||||
|
parser.add_system_program_info("base64"_sv);
|
||||||
|
parser.add_positional_argument(path, "file", "-"_sv);
|
||||||
|
parser.add_switch_argument(decode, 'd', "decode", "decode data");
|
||||||
|
parser.add_switch_argument(allow_garbage, 'i', "ignore-garbage", "when decoding, ignore non-base64 characters");
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
auto file = TRY(os::File::open_input_file(path));
|
||||||
|
|
||||||
|
if (!decode)
|
||||||
|
{
|
||||||
|
auto data = TRY(file->read_all());
|
||||||
|
|
||||||
|
auto encoded = TRY(Base64::encode(data));
|
||||||
|
|
||||||
|
os::println("%s", encoded.chars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto data = TRY(file->read_all_as_string());
|
||||||
|
|
||||||
|
auto decoded = TRY(Base64::decode(data.view(), allow_garbage));
|
||||||
|
|
||||||
|
os::File::standard_output()->write(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
13
apps/cat.cpp
13
apps/cat.cpp
@ -6,14 +6,10 @@ using os::File;
|
|||||||
|
|
||||||
static Result<void> do_cat(StringView path)
|
static Result<void> do_cat(StringView path)
|
||||||
{
|
{
|
||||||
SharedPtr<File> f;
|
SharedPtr<File> f = TRY(File::open_input_file(path));
|
||||||
|
|
||||||
auto out = File::standard_output();
|
auto out = File::standard_output();
|
||||||
|
|
||||||
if (path == "-") f = File::standard_input();
|
|
||||||
else
|
|
||||||
f = TRY(File::open(path, File::ReadOnly));
|
|
||||||
|
|
||||||
auto buf = TRY(Buffer::create_sized(4096));
|
auto buf = TRY(Buffer::create_sized(4096));
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -28,15 +24,18 @@ static Result<void> do_cat(StringView path)
|
|||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView filename;
|
StringView filename;
|
||||||
|
Vector<StringView> files;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Concatenate files to standard output."_sv);
|
parser.add_description("Concatenate files to standard output."_sv);
|
||||||
|
parser.add_system_program_info("cat"_sv);
|
||||||
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
|
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
|
||||||
Vector<StringView> extra_files = TRY(parser.parse(argc, argv));
|
parser.set_vector_argument(files);
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
TRY(do_cat(filename));
|
TRY(do_cat(filename));
|
||||||
|
|
||||||
for (auto file : extra_files) TRY(do_cat(file));
|
for (auto file : files) TRY(do_cat(file));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@ -10,6 +10,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Change the permissions of a file."_sv);
|
parser.add_description("Change the permissions of a file."_sv);
|
||||||
|
parser.add_system_program_info("chmod"_sv);
|
||||||
parser.add_positional_argument(mode_string, "mode"_sv, true);
|
parser.add_positional_argument(mode_string, "mode"_sv, true);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <grp.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -12,6 +13,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Change the owner and group of a file."_sv);
|
parser.add_description("Change the owner and group of a file."_sv);
|
||||||
|
parser.add_system_program_info("chown"_sv);
|
||||||
parser.add_positional_argument(user_and_group, "owner"_sv, true);
|
parser.add_positional_argument(user_and_group, "owner"_sv, true);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
@ -30,9 +32,17 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
if (rc.has_value()) { gid = (u32)rc.value(); }
|
if (rc.has_value()) { gid = (u32)rc.value(); }
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "FIXME: read entry from group file to determine GID for group %s\n", group.chars());
|
struct group* grp = getgrnam(group.chars());
|
||||||
|
if (!grp)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unknown group %s!\n", argv[0], group.chars());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gid = grp->gr_gid;
|
||||||
|
|
||||||
|
endgrent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (names.size() > 0)
|
if (names.size() > 0)
|
||||||
@ -65,6 +75,8 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
gid = pw->pw_gid;
|
gid = pw->pw_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpwent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView date;
|
StringView date;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Display the current (or another) date and time."_sv);
|
parser.add_description("Display the current (or another) date and time."_sv);
|
||||||
|
parser.add_system_program_info("date"_sv);
|
||||||
parser.add_value_argument(date, 'd', "date"_sv, true,
|
parser.add_value_argument(date, 'd', "date"_sv, true,
|
||||||
"the UNIX timestamp to display instead of the current time"_sv);
|
"the UNIX timestamp to display instead of the current time"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
@ -19,5 +19,7 @@ int main(int argc, char** argv)
|
|||||||
if (date.is_empty()) { now = time(NULL); }
|
if (date.is_empty()) { now = time(NULL); }
|
||||||
else { now = strtol(date.chars(), nullptr, 10); }
|
else { now = strtol(date.chars(), nullptr, 10); }
|
||||||
|
|
||||||
fputs(ctime(&now), stdout);
|
os::print("%s", ctime(&now));
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Edit a file using basic line-based shell editing."_sv);
|
parser.add_description("Edit a file using basic line-based shell editing."_sv);
|
||||||
|
parser.add_system_program_info("edit"_sv);
|
||||||
parser.add_positional_argument(pathname, "path"_sv, true);
|
parser.add_positional_argument(pathname, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
131
apps/init.cpp
131
apps/init.cpp
@ -1,12 +1,12 @@
|
|||||||
#include <luna/PathParser.h>
|
|
||||||
#include <luna/String.h>
|
|
||||||
#include <luna/Vector.h>
|
|
||||||
#include <os/File.h>
|
|
||||||
#include <os/Process.h>
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <luna/PathParser.h>
|
||||||
|
#include <luna/Sort.h>
|
||||||
|
#include <luna/String.h>
|
||||||
|
#include <luna/Vector.h>
|
||||||
|
#include <os/Directory.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <os/Process.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -18,36 +18,30 @@
|
|||||||
|
|
||||||
FILE* g_init_log;
|
FILE* g_init_log;
|
||||||
|
|
||||||
#define xmknod(path, mode, maj, min) \
|
|
||||||
if (mknod(path, mode, makedev(maj, min)) < 0) exit(255);
|
|
||||||
|
|
||||||
// Too early for console logs (/dev/console is created here!), so we have to resort to exiting with a weird exit code in
|
|
||||||
// case of failure.
|
|
||||||
static void populate_devfs()
|
|
||||||
{
|
|
||||||
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255);
|
|
||||||
|
|
||||||
xmknod("/dev/console", 0666, 1, 0);
|
|
||||||
xmknod("/dev/null", 0666, 2, 0);
|
|
||||||
xmknod("/dev/zero", 0666, 2, 1);
|
|
||||||
xmknod("/dev/fb0", 0222, 3, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Service
|
struct Service
|
||||||
{
|
{
|
||||||
String name;
|
String name;
|
||||||
String command;
|
String command;
|
||||||
bool restart { false };
|
bool restart { false };
|
||||||
String environment;
|
String environment;
|
||||||
|
String standard_output;
|
||||||
|
String standard_error;
|
||||||
|
String standard_input;
|
||||||
|
bool wait { false };
|
||||||
Option<pid_t> pid {};
|
Option<pid_t> pid {};
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<Service> g_services;
|
Vector<Service> g_services;
|
||||||
|
|
||||||
static Result<void> service_child(const Service& service)
|
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
|
||||||
|
SharedPtr<os::File> input)
|
||||||
{
|
{
|
||||||
auto args = TRY(service.command.split(" \n"));
|
auto args = TRY(service.command.split(" \n"));
|
||||||
|
|
||||||
|
if (output) dup2(output->fd(), STDOUT_FILENO);
|
||||||
|
if (error) dup2(error->fd(), STDERR_FILENO);
|
||||||
|
if (input) dup2(input->fd(), STDIN_FILENO);
|
||||||
|
|
||||||
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
|
if (service.environment.is_empty()) { TRY(os::Process::exec(args[0].view(), args.slice(), false)); }
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -60,10 +54,32 @@ static Result<void> service_child(const Service& service)
|
|||||||
|
|
||||||
static Result<void> try_start_service(Service& service)
|
static Result<void> try_start_service(Service& service)
|
||||||
{
|
{
|
||||||
|
SharedPtr<os::File> new_stdout = {};
|
||||||
|
SharedPtr<os::File> new_stderr = {};
|
||||||
|
SharedPtr<os::File> new_stdin = {};
|
||||||
|
|
||||||
|
if (!service.standard_output.is_empty())
|
||||||
|
{
|
||||||
|
new_stdout = TRY(os::File::open_or_create(service.standard_output.view(), os::File::Append, 0600));
|
||||||
|
new_stdout->set_close_on_exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!service.standard_error.is_empty())
|
||||||
|
{
|
||||||
|
new_stderr = TRY(os::File::open_or_create(service.standard_error.view(), os::File::Append, 0600));
|
||||||
|
new_stderr->set_close_on_exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!service.standard_input.is_empty())
|
||||||
|
{
|
||||||
|
new_stdin = TRY(os::File::open(service.standard_input.view(), os::File::ReadOnly));
|
||||||
|
new_stdin->set_close_on_exec();
|
||||||
|
}
|
||||||
|
|
||||||
pid_t pid = TRY(os::Process::fork());
|
pid_t pid = TRY(os::Process::fork());
|
||||||
if (pid == 0)
|
if (pid == 0)
|
||||||
{
|
{
|
||||||
auto rc = service_child(service);
|
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
|
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
|
||||||
@ -75,6 +91,17 @@ static Result<void> try_start_service(Service& service)
|
|||||||
|
|
||||||
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
|
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
|
||||||
|
|
||||||
|
if (service.wait)
|
||||||
|
{
|
||||||
|
fprintf(g_init_log, "[init] waiting for child process %d to finish\n", pid);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
pid_t id = waitpid(pid, &status, 0);
|
||||||
|
if (id < 0) { return err(errno); }
|
||||||
|
|
||||||
|
fprintf(g_init_log, "[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
else
|
||||||
service.pid = pid;
|
service.pid = pid;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@ -90,9 +117,9 @@ static void start_service(Service& service)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> load_service(StringView path)
|
static Result<void> load_service(const os::Path& path)
|
||||||
{
|
{
|
||||||
fprintf(g_init_log, "[init] reading service file: %s\n", path.chars());
|
fprintf(g_init_log, "[init] reading service file: %s\n", path.name().chars());
|
||||||
|
|
||||||
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||||
|
|
||||||
@ -142,6 +169,35 @@ static Result<void> load_service(StringView path)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parts[0].view() == "StandardOutput")
|
||||||
|
{
|
||||||
|
service.standard_output = move(parts[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0].view() == "StandardError")
|
||||||
|
{
|
||||||
|
service.standard_error = move(parts[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0].view() == "StandardInput")
|
||||||
|
{
|
||||||
|
service.standard_input = move(parts[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0].view() == "Wait")
|
||||||
|
{
|
||||||
|
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
|
||||||
|
{
|
||||||
|
service.wait = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
service.wait = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
|
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,23 +222,12 @@ static Result<void> load_service(StringView path)
|
|||||||
|
|
||||||
static Result<void> load_services()
|
static Result<void> load_services()
|
||||||
{
|
{
|
||||||
DIR* dp = opendir("/etc/init");
|
auto dir = TRY(os::Directory::open("/etc/init"));
|
||||||
if (!dp)
|
|
||||||
{
|
|
||||||
fprintf(g_init_log, "[init] cannot open service directory: %s\n", strerror(errno));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
dirent* ent;
|
auto services = TRY(dir->list(os::Directory::Filter::ParentAndBase));
|
||||||
while ((ent = readdir(dp)))
|
sort(services.begin(), services.end(), String::compare);
|
||||||
{
|
|
||||||
if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue;
|
|
||||||
|
|
||||||
auto service_path = TRY(PathParser::join("/etc/init"_sv, ent->d_name));
|
for (const auto& entry : services) TRY(load_service({ dir->fd(), entry.view() }));
|
||||||
TRY(load_service(service_path.view()));
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dp);
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -221,14 +266,14 @@ int main()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
populate_devfs();
|
|
||||||
|
|
||||||
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
|
// 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/console", "r");
|
stdin = fopen("/dev/console", "r");
|
||||||
stdout = fopen("/dev/console", "w");
|
stdout = fopen("/dev/console", "w");
|
||||||
stderr = fopen("/dev/console", "w");
|
stderr = fopen("/dev/console", "w");
|
||||||
|
|
||||||
|
umask(022);
|
||||||
|
|
||||||
g_init_log = fopen("/init.log", "w+");
|
g_init_log = fopen("/init.log", "w+");
|
||||||
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
|
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
45
apps/ipc-test.cpp
Normal file
45
apps/ipc-test.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int pfds[2];
|
||||||
|
|
||||||
|
if (pipe(pfds) < 0)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t child = fork();
|
||||||
|
if (child == 0)
|
||||||
|
{
|
||||||
|
close(pfds[1]);
|
||||||
|
|
||||||
|
char buffer[4096];
|
||||||
|
size_t nread = read(pfds[0], buffer, sizeof(buffer) - 1);
|
||||||
|
buffer[nread] = 0;
|
||||||
|
close(pfds[0]);
|
||||||
|
|
||||||
|
puts(buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (child == -1)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pfds[0]);
|
||||||
|
|
||||||
|
const char* string = "Hello from a child process who just received this message from its parent!";
|
||||||
|
write(pfds[1], string, strlen(string));
|
||||||
|
close(pfds[1]);
|
||||||
|
|
||||||
|
wait(NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
29
apps/ln.cpp
Normal file
29
apps/ln.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
StringView target;
|
||||||
|
StringView linkpath;
|
||||||
|
|
||||||
|
bool create_symlink { false };
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Create links between files.");
|
||||||
|
parser.add_system_program_info("ln"_sv);
|
||||||
|
parser.add_positional_argument(target, "target", true);
|
||||||
|
parser.add_positional_argument(linkpath, "linkpath", true);
|
||||||
|
parser.add_switch_argument(create_symlink, 's', "symbolic"_sv, "create symlinks instead of hard links"_sv);
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
auto link_function = create_symlink ? symlink : link;
|
||||||
|
|
||||||
|
if (link_function(target.chars(), linkpath.chars()) < 0)
|
||||||
|
{
|
||||||
|
perror("ln");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
45
apps/login.cpp
Normal file
45
apps/login.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <luna/String.h>
|
||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
StringView username;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Begin a new session on the system.");
|
||||||
|
parser.add_system_program_info("login"_sv);
|
||||||
|
parser.add_positional_argument(username, "user", false);
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (geteuid() != 0)
|
||||||
|
{
|
||||||
|
os::eprintln("error: login must be run as root.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
if (username.is_empty())
|
||||||
|
{
|
||||||
|
auto input = os::File::standard_input();
|
||||||
|
|
||||||
|
os::print("Username: ");
|
||||||
|
|
||||||
|
name = TRY(input->read_line());
|
||||||
|
name.trim("\n");
|
||||||
|
|
||||||
|
if (name.is_empty()) return 0;
|
||||||
|
|
||||||
|
username = name.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
execl("/bin/su", "login", "-lp", "--", username.chars(), nullptr);
|
||||||
|
|
||||||
|
perror("su");
|
||||||
|
return 1;
|
||||||
|
}
|
114
apps/ls.cpp
114
apps/ls.cpp
@ -1,42 +1,114 @@
|
|||||||
|
#include <grp.h>
|
||||||
|
#include <luna/Units.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/Directory.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <os/FileSystem.h>
|
||||||
|
#include <os/Mode.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
void find_user_and_group(struct stat& st, StringView& owner, StringView& group)
|
||||||
#include <fcntl.h>
|
{
|
||||||
#include <stdio.h>
|
auto* pw = getpwuid(st.st_uid);
|
||||||
#include <unistd.h>
|
if (!pw) owner = "???";
|
||||||
|
else
|
||||||
|
owner = pw->pw_name;
|
||||||
|
|
||||||
|
auto* grp = getgrgid(st.st_gid);
|
||||||
|
if (!grp) group = "???";
|
||||||
|
else
|
||||||
|
group = grp->gr_name;
|
||||||
|
}
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView pathname;
|
StringView pathname;
|
||||||
bool show_all { false };
|
bool show_all { false };
|
||||||
bool show_almost_all { false };
|
bool show_almost_all { false };
|
||||||
|
bool long_list { false };
|
||||||
|
bool human_readable { false };
|
||||||
|
bool si { false };
|
||||||
|
bool follow_symlink_args { false };
|
||||||
|
bool one_per_line { false };
|
||||||
|
bool list_directories { false };
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
|
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
|
||||||
|
parser.add_system_program_info("ls"_sv);
|
||||||
parser.add_positional_argument(pathname, "directory"_sv, "."_sv);
|
parser.add_positional_argument(pathname, "directory"_sv, "."_sv);
|
||||||
parser.add_switch_argument(show_all, 'a', "all"_sv, "also list hidden files (whose filename begins with a dot)"_sv);
|
parser.add_switch_argument(show_all, 'a', "all"_sv, "also list hidden files (whose filename begins with a dot)"_sv);
|
||||||
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv);
|
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv);
|
||||||
|
parser.add_switch_argument(long_list, 'l', ""_sv, "use a long listing format"_sv);
|
||||||
|
parser.add_switch_argument(human_readable, 'h', "human-readable"_sv,
|
||||||
|
"with -l, show human-readable sizes e.g. 2KiB, 6GiB"_sv);
|
||||||
|
parser.add_switch_argument(si, ' ', "si"_sv, "same as -h, but show sizes in powers of 10"_sv);
|
||||||
|
parser.add_switch_argument(follow_symlink_args, 'H', "dereference-args"_sv,
|
||||||
|
"follow symbolic links listed as arguments"_sv);
|
||||||
|
parser.add_switch_argument(one_per_line, '1', ""_sv, "list one file per line"_sv);
|
||||||
|
parser.add_switch_argument(list_directories, 'd', "directory"_sv, "list directories instead of their contents"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
DIR* dp = opendir(pathname.chars());
|
Vector<String> files;
|
||||||
if (!dp)
|
int dirfd = AT_FDCWD;
|
||||||
|
SharedPtr<os::Directory> dir;
|
||||||
|
|
||||||
|
if (!long_list) follow_symlink_args = true;
|
||||||
|
|
||||||
|
if (os::FileSystem::is_directory(pathname, follow_symlink_args) && !list_directories)
|
||||||
{
|
{
|
||||||
perror("opendir");
|
dir = TRY(os::Directory::open(pathname));
|
||||||
return 1;
|
dirfd = dir->fd();
|
||||||
|
|
||||||
|
auto filter = os::Directory::Filter::Hidden;
|
||||||
|
if (show_almost_all) filter = os::Directory::Filter::ParentAndBase;
|
||||||
|
else if (show_all)
|
||||||
|
filter = os::Directory::Filter::None;
|
||||||
|
|
||||||
|
files = TRY(dir->list(filter));
|
||||||
|
}
|
||||||
|
else if (os::FileSystem::exists(pathname, follow_symlink_args))
|
||||||
|
{
|
||||||
|
auto str = TRY(String::from_string_view(pathname));
|
||||||
|
TRY(files.try_append(move(str)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return err(ENOENT);
|
||||||
|
|
||||||
|
if (!long_list)
|
||||||
|
{
|
||||||
|
auto list = TRY(String::join(files, one_per_line ? "\n"_sv : " "_sv));
|
||||||
|
if (!list.is_empty()) os::println("%s", list.chars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const auto& file : files)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
TRY(os::FileSystem::stat({ dirfd, file.view() }, st, false));
|
||||||
|
|
||||||
|
auto link = TRY(os::FileSystem::readlink({ dirfd, file.view() }));
|
||||||
|
|
||||||
|
StringView owner;
|
||||||
|
StringView group;
|
||||||
|
|
||||||
|
find_user_and_group(st, owner, group);
|
||||||
|
|
||||||
|
char formatted_mode[11];
|
||||||
|
os::format_mode(st.st_mode, formatted_mode);
|
||||||
|
|
||||||
|
if (!human_readable && !si)
|
||||||
|
{
|
||||||
|
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||||
|
st.st_size, file.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
|
||||||
|
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||||
|
size.chars(), file.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int first_ent = 1;
|
|
||||||
do {
|
|
||||||
struct dirent* ent = readdir(dp);
|
|
||||||
if (!ent) break;
|
|
||||||
if (show_almost_all && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) continue;
|
|
||||||
if (!show_all && !show_almost_all && *ent->d_name == '.') continue;
|
|
||||||
printf(first_ent ? "%s" : " %s", ent->d_name);
|
|
||||||
first_ent = 0;
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
putchar('\n');
|
|
||||||
|
|
||||||
closedir(dp);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static mode_t s_umask = 0;
|
||||||
|
|
||||||
|
static void read_umask()
|
||||||
|
{
|
||||||
|
s_umask = umask(0);
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> mkdir_recursively(StringView path, mode_t mode)
|
Result<void> mkdir_recursively(StringView path, mode_t mode)
|
||||||
{
|
{
|
||||||
@ -15,7 +23,7 @@ begin:
|
|||||||
PathParser parser = TRY(PathParser::create(path.chars()));
|
PathParser parser = TRY(PathParser::create(path.chars()));
|
||||||
auto parent = TRY(parser.dirname());
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
TRY(mkdir_recursively(parent.view(), mode));
|
TRY(mkdir_recursively(parent.view(), (0777 & ~s_umask) | S_IWUSR | S_IXUSR));
|
||||||
|
|
||||||
goto begin;
|
goto begin;
|
||||||
}
|
}
|
||||||
@ -31,13 +39,20 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Create directories."_sv);
|
parser.add_description("Create directories."_sv);
|
||||||
|
parser.add_system_program_info("mkdir"_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.add_positional_argument(mode_string, "mode"_sv, "755"_sv);
|
parser.add_value_argument(mode_string, 'm', "mode"_sv, true, "set the mode for the newly created directory");
|
||||||
parser.add_switch_argument(recursive, 'p', "parents"_sv,
|
parser.add_switch_argument(recursive, 'p', "parents"_sv,
|
||||||
"if parent directories do not exist, create them as well"_sv);
|
"if parent directories do not exist, create them as well"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
mode_t mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8);
|
read_umask();
|
||||||
|
|
||||||
|
mode_t mode;
|
||||||
|
|
||||||
|
if (mode_string.is_empty()) mode = 0777 & ~s_umask;
|
||||||
|
else
|
||||||
|
mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8) & 01777;
|
||||||
|
|
||||||
if (recursive)
|
if (recursive)
|
||||||
{
|
{
|
||||||
|
24
apps/mount.cpp
Normal file
24
apps/mount.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
StringView target;
|
||||||
|
StringView fstype;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Mount a file system.");
|
||||||
|
parser.add_system_program_info("mount"_sv);
|
||||||
|
parser.add_positional_argument(target, "mountpoint"_sv, true);
|
||||||
|
parser.add_value_argument(fstype, 't', "type"_sv, "auto"_sv, "the file system type to use");
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (mount(target.chars(), fstype.chars()) < 0)
|
||||||
|
{
|
||||||
|
perror("mount");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
44
apps/ps.cpp
Normal file
44
apps/ps.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sys/pstat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Show a list of processes on the system.");
|
||||||
|
parser.add_system_program_info("ps"_sv);
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
os::println("UID PID PPID TIME CMD");
|
||||||
|
|
||||||
|
pid_t last = pstat(-1, nullptr);
|
||||||
|
for (pid_t pid = 1; pid <= last; pid++)
|
||||||
|
{
|
||||||
|
static process ps;
|
||||||
|
if (pstat(pid, &ps) < 0)
|
||||||
|
{
|
||||||
|
if (errno == ESRCH) continue;
|
||||||
|
return err(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&ps.ps_time.tv_sec, &tm);
|
||||||
|
|
||||||
|
char timebuf[256];
|
||||||
|
strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
|
||||||
|
|
||||||
|
const char* user = "???";
|
||||||
|
|
||||||
|
passwd* pw = getpwuid(ps.ps_uid);
|
||||||
|
if (pw) user = pw->pw_name;
|
||||||
|
|
||||||
|
os::println("%-8s %6d %6d %10s %s", user, ps.ps_pid, ps.ps_ppid, timebuf, ps.ps_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -8,6 +8,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Remove a path from the file system."_sv);
|
parser.add_description("Remove a path from the file system."_sv);
|
||||||
|
parser.add_system_program_info("rm"_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
|
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
|
||||||
"remove a directory recursively (by default, rm removes only empty directories)"_sv);
|
"remove a directory recursively (by default, rm removes only empty directories)"_sv);
|
||||||
|
15
apps/sh.cpp
15
apps/sh.cpp
@ -1,11 +1,10 @@
|
|||||||
|
#include <errno.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
#include <os/Process.h>
|
#include <os/Process.h>
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -46,6 +45,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("The Luna system's command shell."_sv);
|
parser.add_description("The Luna system's command shell."_sv);
|
||||||
|
parser.add_system_program_info("sh"_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, "-"_sv);
|
parser.add_positional_argument(path, "path"_sv, "-"_sv);
|
||||||
parser.add_value_argument(command, 'c', "command"_sv, true, "execute a single command and then exit"_sv);
|
parser.add_value_argument(command, 'c', "command"_sv, true, "execute a single command and then exit"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
@ -70,8 +70,11 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
hostname = g_sysinfo.nodename;
|
hostname = g_sysinfo.nodename;
|
||||||
|
|
||||||
if (getuid() == 0) prompt_end = '#';
|
if (getuid() == 0) prompt_end = '#';
|
||||||
|
|
||||||
struct passwd* pw = getpwuid(getuid());
|
struct passwd* pw = getpwuid(getuid());
|
||||||
if (pw) { username = pw->pw_name; }
|
if (pw) { username = pw->pw_name; }
|
||||||
|
else { username = getenv("USER"); }
|
||||||
|
endpwent();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
@ -79,11 +82,15 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
if (interactive)
|
if (interactive)
|
||||||
{
|
{
|
||||||
auto cwd = TRY(os::FileSystem::working_directory());
|
auto cwd = TRY(os::FileSystem::working_directory());
|
||||||
printf("%s@%s:%s%c ", username, hostname, cwd.chars(), prompt_end);
|
os::print("%s@%s:%s%c ", username, hostname, cwd.chars(), prompt_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cmd = TRY(input_file->read_line());
|
auto cmd = TRY(input_file->read_line());
|
||||||
if (cmd.is_empty()) break;
|
if (cmd.is_empty())
|
||||||
|
{
|
||||||
|
if (interactive) puts("exit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (strspn(cmd.chars(), " \n") == cmd.length()) continue;
|
if (strspn(cmd.chars(), " \n") == cmd.length()) continue;
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/FileSystem.h>
|
||||||
|
#include <os/Mode.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
@ -9,6 +11,9 @@ static const char* file_type(mode_t mode)
|
|||||||
case S_IFREG: return "regular file";
|
case S_IFREG: return "regular file";
|
||||||
case S_IFDIR: return "directory";
|
case S_IFDIR: return "directory";
|
||||||
case S_IFCHR: return "character special device";
|
case S_IFCHR: return "character special device";
|
||||||
|
case S_IFBLK: return "block special device";
|
||||||
|
case S_IFLNK: return "symbolic link";
|
||||||
|
case S_IFIFO: return "pipe";
|
||||||
default: return "unknown file type";
|
default: return "unknown file type";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,23 +21,25 @@ static const char* file_type(mode_t mode)
|
|||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView path;
|
StringView path;
|
||||||
|
bool follow_symlinks { false };
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Display file status.");
|
parser.add_description("Display file status.");
|
||||||
|
parser.add_system_program_info("stat"_sv);
|
||||||
parser.add_positional_argument(path, "path", true);
|
parser.add_positional_argument(path, "path", true);
|
||||||
|
parser.add_switch_argument(follow_symlinks, 'L', "dereference"_sv, "follow symlinks");
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.chars(), &st) < 0)
|
TRY(os::FileSystem::stat(path, st, follow_symlinks));
|
||||||
{
|
|
||||||
perror("stat");
|
char buf[11];
|
||||||
return 1;
|
os::format_mode(st.st_mode, buf);
|
||||||
}
|
|
||||||
|
|
||||||
printf(" File: %s\n", path.chars());
|
printf(" File: %s\n", path.chars());
|
||||||
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));
|
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));
|
||||||
printf("Inode: %lu Links: %lu\n", st.st_ino, st.st_nlink);
|
printf("Inode: %lu Links: %lu\n", st.st_ino, st.st_nlink);
|
||||||
printf(" Mode: %#o UID: %u GID: %u\n", st.st_mode & ~S_IFMT, st.st_uid, st.st_gid);
|
printf(" Mode: (%#o/%s) UID: %u GID: %u\n", st.st_mode & ~S_IFMT, buf, st.st_uid, st.st_gid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
28
apps/su.cpp
28
apps/su.cpp
@ -33,7 +33,7 @@ char* getpass()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char buf[1024];
|
static char buf[BUFSIZ];
|
||||||
char* rc = fgets(buf, sizeof(buf), stdin);
|
char* rc = fgets(buf, sizeof(buf), stdin);
|
||||||
|
|
||||||
restore_terminal();
|
restore_terminal();
|
||||||
@ -54,33 +54,40 @@ char* getpass()
|
|||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView name;
|
StringView name;
|
||||||
|
bool prompt_password;
|
||||||
|
bool login;
|
||||||
|
|
||||||
if (geteuid() != 0)
|
if (geteuid() != 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "su must be setuid root!\n");
|
fprintf(stderr, "%s must be setuid root!\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Switch to a different user (by default, root)."_sv);
|
parser.add_description("Switch to a different user (by default, root)."_sv);
|
||||||
|
parser.add_system_program_info("su"_sv);
|
||||||
parser.add_positional_argument(name, "name"_sv, "root"_sv);
|
parser.add_positional_argument(name, "name"_sv, "root"_sv);
|
||||||
|
parser.add_switch_argument(prompt_password, 'p', "prompt", "prompt for a password even if running as root");
|
||||||
|
parser.add_switch_argument(login, 'l', "login"_sv, "change directory to the user's home and start a login shell");
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
struct passwd* entry = getpwnam(name.chars());
|
struct passwd* entry = getpwnam(name.chars());
|
||||||
if (!entry)
|
if (!entry)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "su: user %s not found!\n", name.chars());
|
fprintf(stderr, "%s: user %s not found!\n", argv[0], name.chars());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getuid() != geteuid() && *entry->pw_passwd)
|
endpwent();
|
||||||
|
|
||||||
|
if ((prompt_password || getuid() != geteuid()) && *entry->pw_passwd)
|
||||||
{
|
{
|
||||||
char* pass = getpass();
|
char* pass = getpass();
|
||||||
if (!pass) return 1;
|
if (!pass) return 1;
|
||||||
|
|
||||||
if (strcmp(pass, entry->pw_passwd))
|
if (strcmp(pass, entry->pw_passwd))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Wrong password!\n");
|
fprintf(stderr, "%s: wrong password!\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +97,20 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
setgid(entry->pw_gid);
|
setgid(entry->pw_gid);
|
||||||
setuid(entry->pw_uid);
|
setuid(entry->pw_uid);
|
||||||
|
|
||||||
|
if (login)
|
||||||
|
{
|
||||||
chdir(entry->pw_dir);
|
chdir(entry->pw_dir);
|
||||||
|
clearenv();
|
||||||
|
setenv("PATH", "/bin:/sbin", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (login || entry->pw_uid != 0) setenv("USER", entry->pw_name, 1);
|
||||||
|
|
||||||
|
setenv("HOME", entry->pw_dir, 1);
|
||||||
|
setenv("SHELL", entry->pw_shell, 1);
|
||||||
|
|
||||||
execl(entry->pw_shell, entry->pw_shell, NULL);
|
execl(entry->pw_shell, entry->pw_shell, NULL);
|
||||||
|
|
||||||
|
perror("execl");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
45
apps/time.cpp
Normal file
45
apps/time.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <os/Process.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
Vector<StringView> command;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Time a command.");
|
||||||
|
parser.add_system_program_info("time"_sv);
|
||||||
|
parser.set_vector_argument(command, true);
|
||||||
|
TRY(parser.parse(argc, argv));
|
||||||
|
|
||||||
|
auto pid = TRY(os::Process::fork());
|
||||||
|
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
TRY(os::Process::exec(command[0], command.slice()));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitpid(pid, nullptr, 0) < 0)
|
||||||
|
{
|
||||||
|
perror("waitpid");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rusage usage;
|
||||||
|
if (getrusage(RUSAGE_CHILDREN, &usage) < 0)
|
||||||
|
{
|
||||||
|
perror("getrusage");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmdline = TRY(String::join(command, " "));
|
||||||
|
|
||||||
|
os::println("%s %d.%.2ds user %d.%.2ds system"_sv, cmdline.chars(), usage.ru_utime.tv_sec,
|
||||||
|
usage.ru_utime.tv_usec / 10000, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 10000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
22
apps/umount.cpp
Normal file
22
apps/umount.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
StringView target;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Unmount a file system.");
|
||||||
|
parser.add_system_program_info("umount"_sv);
|
||||||
|
parser.add_positional_argument(target, "mountpoint"_sv, true);
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (umount(target.chars()) < 0)
|
||||||
|
{
|
||||||
|
perror("umount");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -24,6 +24,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Print system information. If given no options, defaults to -s."_sv);
|
parser.add_description("Print system information. If given no options, defaults to -s."_sv);
|
||||||
|
parser.add_system_program_info("uname"_sv);
|
||||||
parser.add_switch_argument(all, 'a', "all"_sv, "print all information"_sv);
|
parser.add_switch_argument(all, 'a', "all"_sv, "print all information"_sv);
|
||||||
parser.add_switch_argument(kernel_name, 's', "kernel-name"_sv, "print the kernel name"_sv);
|
parser.add_switch_argument(kernel_name, 's', "kernel-name"_sv, "print the kernel name"_sv);
|
||||||
parser.add_switch_argument(node_name, 'n', "nodename"_sv, "print the network hostname"_sv);
|
parser.add_switch_argument(node_name, 'n', "nodename"_sv, "print the network hostname"_sv);
|
||||||
|
3
initrd/etc/group
Normal file
3
initrd/etc/group
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
root:!:0:
|
||||||
|
users:!:1:
|
||||||
|
selene:!:1000:
|
3
initrd/etc/init/00-motd
Normal file
3
initrd/etc/init/00-motd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Name=motd
|
||||||
|
Command=/bin/cat /etc/motd
|
||||||
|
Wait=true
|
3
initrd/etc/init/99-login
Normal file
3
initrd/etc/init/99-login
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Name=login
|
||||||
|
Command=/bin/login
|
||||||
|
Restart=true
|
@ -1,4 +0,0 @@
|
|||||||
Name=shell
|
|
||||||
Command=/bin/sh
|
|
||||||
Restart=true
|
|
||||||
Environment=PATH=/bin:/sbin
|
|
@ -1 +1,4 @@
|
|||||||
Hello, world!
|
Welcome to the Luna system!
|
||||||
|
Have a look around, you can run 'ls /bin' to list available commands.
|
||||||
|
|
||||||
|
You can use the username 'selene' with the password 'moon' to start a user session.
|
||||||
|
@ -31,22 +31,27 @@ set(SOURCES
|
|||||||
src/sys/file.cpp
|
src/sys/file.cpp
|
||||||
src/sys/id.cpp
|
src/sys/id.cpp
|
||||||
src/sys/mkdir.cpp
|
src/sys/mkdir.cpp
|
||||||
src/sys/mknod.cpp
|
src/sys/pstat.cpp
|
||||||
src/sys/waitpid.cpp
|
src/sys/waitpid.cpp
|
||||||
src/sys/getdents.cpp
|
src/sys/getdents.cpp
|
||||||
src/sys/stat.cpp
|
src/sys/stat.cpp
|
||||||
src/sys/chdir.cpp
|
src/sys/chdir.cpp
|
||||||
src/sys/link.cpp
|
src/sys/link.cpp
|
||||||
src/sys/uname.cpp
|
src/sys/uname.cpp
|
||||||
|
src/sys/mount.cpp
|
||||||
|
src/sys/resource.cpp
|
||||||
src/fs/VFS.cpp
|
src/fs/VFS.cpp
|
||||||
|
src/fs/Pipe.cpp
|
||||||
|
src/fs/Mount.cpp
|
||||||
src/fs/tmpfs/FileSystem.cpp
|
src/fs/tmpfs/FileSystem.cpp
|
||||||
src/fs/devices/DeviceRegistry.cpp
|
src/fs/devices/DeviceRegistry.cpp
|
||||||
src/fs/devices/NullDevice.cpp
|
src/fs/devices/NullDevice.cpp
|
||||||
src/fs/devices/ZeroDevice.cpp
|
src/fs/devices/ZeroDevice.cpp
|
||||||
|
src/fs/devices/FullDevice.cpp
|
||||||
src/fs/devices/ConsoleDevice.cpp
|
src/fs/devices/ConsoleDevice.cpp
|
||||||
src/fs/devices/FramebufferDevice.cpp
|
src/fs/devices/FramebufferDevice.cpp
|
||||||
src/InitRD.cpp
|
src/fs/InitRD.cpp
|
||||||
src/ELF.cpp
|
src/thread/ELF.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if("${LUNA_ARCH}" MATCHES "x86_64")
|
if("${LUNA_ARCH}" MATCHES "x86_64")
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
#include <luna/StringView.h>
|
||||||
|
|
||||||
struct Registers;
|
struct Registers;
|
||||||
|
|
||||||
namespace CPU
|
namespace CPU
|
||||||
{
|
{
|
||||||
Result<const char*> identify();
|
Result<StringView> identify();
|
||||||
const char* platform_string();
|
StringView platform_string();
|
||||||
|
|
||||||
void platform_init();
|
void platform_init();
|
||||||
void platform_finish_init();
|
void platform_finish_init();
|
||||||
@ -28,7 +29,5 @@ namespace CPU
|
|||||||
|
|
||||||
u16 get_processor_id();
|
u16 get_processor_id();
|
||||||
|
|
||||||
[[noreturn]] void bootstrap_switch_stack(u64 stack, void* function);
|
|
||||||
|
|
||||||
void pause();
|
void pause();
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace Timer
|
|||||||
{
|
{
|
||||||
void tick()
|
void tick()
|
||||||
{
|
{
|
||||||
timer_ticks++;
|
timer_ticks += ARCH_TIMER_RESOLUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize raw_ticks()
|
usize raw_ticks()
|
||||||
@ -68,20 +68,17 @@ namespace Timer
|
|||||||
|
|
||||||
usize ticks()
|
usize ticks()
|
||||||
{
|
{
|
||||||
return ticks_ms() / 1000;
|
return ticks_us() / US_PER_SECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize ticks_ms()
|
usize ticks_ms()
|
||||||
{
|
{
|
||||||
return timer_ticks / ARCH_TIMER_FREQ;
|
return timer_ticks / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize ticks_us() // We want a bit of precision; if there are 10 ticks/ms, do not return the truncated ms value *
|
usize ticks_us()
|
||||||
// 1000, but ticks * 100 (1000/10), which is more precise
|
|
||||||
{
|
{
|
||||||
if constexpr (ARCH_TIMER_FREQ > 1000) return timer_ticks / (ARCH_TIMER_FREQ / 1000);
|
return timer_ticks;
|
||||||
else
|
|
||||||
return timer_ticks * (1000 / ARCH_TIMER_FREQ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
usize ticks_ns()
|
usize ticks_ns()
|
||||||
@ -91,22 +88,22 @@ namespace Timer
|
|||||||
|
|
||||||
usize boot()
|
usize boot()
|
||||||
{
|
{
|
||||||
return boot_timestamp;
|
return boot_timestamp / US_PER_SECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize boot_ms()
|
usize boot_ms()
|
||||||
{
|
{
|
||||||
return boot_timestamp * MS_PER_SECOND;
|
return boot_timestamp / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize boot_us()
|
usize boot_us()
|
||||||
{
|
{
|
||||||
return boot_timestamp * US_PER_SECOND;
|
return boot_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize boot_ns()
|
usize boot_ns()
|
||||||
{
|
{
|
||||||
return boot_timestamp * NS_PER_SECOND;
|
return boot_timestamp * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize clock()
|
usize clock()
|
||||||
@ -131,14 +128,12 @@ namespace Timer
|
|||||||
|
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
boot_timestamp = bootloader_time_to_unix(bootboot.datetime);
|
boot_timestamp = bootloader_time_to_unix(bootboot.datetime) * US_PER_SECOND;
|
||||||
arch_init();
|
arch_init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(IsPowerOfTwo<usize, ARCH_TIMER_FREQ>, "ARCH_TIMER_FREQ must be a power of two");
|
|
||||||
|
|
||||||
bool should_invoke_scheduler()
|
bool should_invoke_scheduler()
|
||||||
{
|
{
|
||||||
return (timer_ticks % ARCH_TIMER_FREQ) == 0;
|
return (timer_ticks % 1000) == 0;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ extern void pic_eoi(unsigned char irq);
|
|||||||
extern void pic_eoi(Registers* regs);
|
extern void pic_eoi(Registers* regs);
|
||||||
extern void setup_idt();
|
extern void setup_idt();
|
||||||
|
|
||||||
|
static Thread* g_io_thread;
|
||||||
|
|
||||||
void FPData::save()
|
void FPData::save()
|
||||||
{
|
{
|
||||||
asm volatile("fxsave (%0)" : : "r"(m_data));
|
asm volatile("fxsave (%0)" : : "r"(m_data));
|
||||||
@ -126,10 +128,10 @@ void io_thread()
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
u8 scancode;
|
u8 scancode;
|
||||||
while (!scancode_queue.try_pop(scancode)) { kernel_sleep(10); }
|
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
|
||||||
|
|
||||||
char key;
|
char key;
|
||||||
if (Keyboard::decode_scancode(scancode).try_set_value(key)) { ConsoleDevice::did_press_key(key); }
|
if (Keyboard::decode_scancode(scancode).try_set_value(key)) ConsoleDevice::did_press_key(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +149,7 @@ extern "C" void arch_interrupt_entry(Registers* regs)
|
|||||||
{
|
{
|
||||||
u8 scancode = IO::inb(0x60);
|
u8 scancode = IO::inb(0x60);
|
||||||
scancode_queue.try_push(scancode);
|
scancode_queue.try_push(scancode);
|
||||||
|
g_io_thread->wake_up();
|
||||||
pic_eoi(regs);
|
pic_eoi(regs);
|
||||||
}
|
}
|
||||||
else if (regs->isr == 66) // System call
|
else if (regs->isr == 66) // System call
|
||||||
@ -184,7 +187,7 @@ static bool test_nx()
|
|||||||
|
|
||||||
namespace CPU
|
namespace CPU
|
||||||
{
|
{
|
||||||
Result<const char*> identify()
|
Result<StringView> identify()
|
||||||
{
|
{
|
||||||
static char brand_string[49];
|
static char brand_string[49];
|
||||||
|
|
||||||
@ -198,12 +201,12 @@ namespace CPU
|
|||||||
|
|
||||||
brand_string[48] = 0; // null-terminate it :)
|
brand_string[48] = 0; // null-terminate it :)
|
||||||
|
|
||||||
return brand_string;
|
return StringView { brand_string, 48 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* platform_string()
|
StringView platform_string()
|
||||||
{
|
{
|
||||||
return "x86_64";
|
return "x86_64"_sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void platform_init()
|
void platform_init()
|
||||||
@ -219,7 +222,7 @@ namespace CPU
|
|||||||
|
|
||||||
void platform_finish_init()
|
void platform_finish_init()
|
||||||
{
|
{
|
||||||
Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
|
g_io_thread = Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
|
||||||
.expect_value("Could not create the IO background thread!");
|
.expect_value("Could not create the IO background thread!");
|
||||||
|
|
||||||
remap_pic();
|
remap_pic();
|
||||||
@ -273,8 +276,9 @@ namespace CPU
|
|||||||
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
|
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
|
||||||
{
|
{
|
||||||
StackFrame* current_frame = (StackFrame*)base_pointer;
|
StackFrame* current_frame = (StackFrame*)base_pointer;
|
||||||
// FIXME: Validate that the frame itself is readable, might span across multiple pages
|
while (current_frame &&
|
||||||
while (current_frame && MemoryManager::validate_readable_page((u64)current_frame) && current_frame->instruction)
|
MemoryManager::validate_access(current_frame, sizeof(*current_frame), MemoryManager::DEFAULT_ACCESS) &&
|
||||||
|
current_frame->instruction)
|
||||||
{
|
{
|
||||||
callback(current_frame->instruction, arg);
|
callback(current_frame->instruction, arg);
|
||||||
current_frame = current_frame->next;
|
current_frame = current_frame->next;
|
||||||
@ -322,15 +326,6 @@ namespace CPU
|
|||||||
&frame_index);
|
&frame_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void bootstrap_switch_stack(u64 stack, void* function)
|
|
||||||
{
|
|
||||||
asm volatile("mov %0, %%rsp\n"
|
|
||||||
"jmp *%1"
|
|
||||||
:
|
|
||||||
: "r"(stack), "r"(function));
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pause()
|
void pause()
|
||||||
{
|
{
|
||||||
asm volatile("pause");
|
asm volatile("pause");
|
||||||
|
@ -7,8 +7,8 @@ const u64 base_frequency = 1193182;
|
|||||||
|
|
||||||
void Timer::arch_init()
|
void Timer::arch_init()
|
||||||
{
|
{
|
||||||
constexpr u16 divisor = (u16)(base_frequency / (ARCH_TIMER_FREQ * 1000));
|
constexpr u16 divisor = (u16)(base_frequency / ((1000 / ARCH_TIMER_RESOLUTION) * 1000));
|
||||||
static_assert(divisor >= 100, "ARCH_TIMER_FREQ is too high");
|
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));
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
const usize ARCH_TIMER_FREQ = 4;
|
// Every timer tick is equivalent to 250 microseconds.
|
||||||
|
const usize ARCH_TIMER_RESOLUTION = 250;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "boot/Init.h"
|
#include "boot/Init.h"
|
||||||
#include "InitRD.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include "boot/bootboot.h"
|
#include "boot/bootboot.h"
|
||||||
|
#include "fs/InitRD.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "video/Framebuffer.h"
|
#include "video/Framebuffer.h"
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
@ -18,7 +18,7 @@ void InitRD::initialize()
|
|||||||
g_initrd.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
|
g_initrd.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode, uid_t uid, gid_t gid)
|
||||||
{
|
{
|
||||||
auto rc = VFS::create_directory(path, Credentials {});
|
auto rc = VFS::create_directory(path, Credentials {});
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
@ -26,7 +26,9 @@ static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
|||||||
if (rc.error() == EEXIST) return {};
|
if (rc.error() == EEXIST) return {};
|
||||||
return rc.release_error();
|
return rc.release_error();
|
||||||
}
|
}
|
||||||
rc.value()->chmod(mode & (mode_t)~S_IFMT);
|
auto dir = rc.value();
|
||||||
|
dir->chmod(mode & (mode_t)~S_IFMT);
|
||||||
|
dir->chown(uid, gid);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,13 +39,14 @@ Result<void> InitRD::populate_vfs()
|
|||||||
{
|
{
|
||||||
if (entry.type == TarStream::EntryType::RegularFile)
|
if (entry.type == TarStream::EntryType::RegularFile)
|
||||||
{
|
{
|
||||||
auto file = TRY(VFS::create_file(entry.name, Credentials {}));
|
auto file = TRY(VFS::create_file(entry.name.chars(), Credentials {}));
|
||||||
file->write(entry.data(), 0, entry.size);
|
file->write(entry.data(), 0, entry.size);
|
||||||
file->chmod(entry.mode & (mode_t)~S_IFMT);
|
file->chmod(entry.mode & (mode_t)~S_IFMT);
|
||||||
|
file->chown(entry.uid, entry.gid);
|
||||||
}
|
}
|
||||||
else if (entry.type == TarStream::EntryType::Directory)
|
else if (entry.type == TarStream::EntryType::Directory)
|
||||||
{
|
{
|
||||||
TRY(vfs_create_dir_if_not_exists(entry.name, entry.mode));
|
TRY(vfs_create_dir_if_not_exists(entry.name.chars(), entry.mode, entry.uid, entry.gid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
kernel/src/fs/Mount.cpp
Normal file
26
kernel/src/fs/Mount.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "fs/Mount.h"
|
||||||
|
|
||||||
|
LinkedList<MountInode> g_mounts;
|
||||||
|
|
||||||
|
Result<SharedPtr<VFS::Inode>> MountInode::create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs)
|
||||||
|
{
|
||||||
|
auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) MountInode()));
|
||||||
|
|
||||||
|
inode->m_source = source;
|
||||||
|
inode->m_mountee = fs;
|
||||||
|
inode->m_mount_root_inode = fs->root_inode();
|
||||||
|
|
||||||
|
auto parent = TRY(source->find(".."));
|
||||||
|
TRY(fs->set_mount_dir(parent));
|
||||||
|
|
||||||
|
source->add_handle();
|
||||||
|
|
||||||
|
g_mounts.append(inode.ptr());
|
||||||
|
|
||||||
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
MountInode::~MountInode()
|
||||||
|
{
|
||||||
|
m_source->remove_handle();
|
||||||
|
}
|
155
kernel/src/fs/Mount.h
Normal file
155
kernel/src/fs/Mount.h
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fs/VFS.h"
|
||||||
|
#include <luna/LinkedList.h>
|
||||||
|
|
||||||
|
class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Result<SharedPtr<VFS::Inode>> create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs);
|
||||||
|
|
||||||
|
void set_fs(SharedPtr<VFS::FileSystem> fs)
|
||||||
|
{
|
||||||
|
m_mountee = fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<VFS::Inode>> find(const char* name) const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Option<VFS::DirectoryEntry> get(usize index) const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> read(u8*, usize, usize) const override
|
||||||
|
{
|
||||||
|
return err(EISDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> write(const u8*, usize, usize) override
|
||||||
|
{
|
||||||
|
return err(EISDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> truncate(usize) override
|
||||||
|
{
|
||||||
|
return err(EISDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blocking() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize size() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t mode() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 uid() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->uid();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 gid() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->gid();
|
||||||
|
}
|
||||||
|
|
||||||
|
nlink_t nlinks() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->nlinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chmod(mode_t mode) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->chmod(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chown(u32 uid, u32 gid) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->chown(uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::FileSystem* fs() const override
|
||||||
|
{
|
||||||
|
return m_mountee.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
usize inode_number() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->inode_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::InodeType type() const override
|
||||||
|
{
|
||||||
|
return VFS::InodeType::Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_link() override
|
||||||
|
{
|
||||||
|
m_mount_root_inode->did_link();
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_unlink() override
|
||||||
|
{
|
||||||
|
m_mount_root_inode->did_unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
usize entries() const override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_mountpoint() const override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<VFS::Inode> source() const
|
||||||
|
{
|
||||||
|
return m_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> remove_entry(const char* name) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->remove_entry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->create_file(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->create_subdirectory(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->add_entry(inode, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name) override
|
||||||
|
{
|
||||||
|
return m_mount_root_inode->replace_entry(inode, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MountInode();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SharedPtr<VFS::Inode> m_source;
|
||||||
|
SharedPtr<VFS::FileSystem> m_mountee;
|
||||||
|
SharedPtr<VFS::Inode> m_mount_root_inode;
|
||||||
|
|
||||||
|
MountInode() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern LinkedList<MountInode> g_mounts;
|
62
kernel/src/fs/Pipe.cpp
Normal file
62
kernel/src/fs/Pipe.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "fs/Pipe.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
|
||||||
|
Result<void> Pipe::create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe)
|
||||||
|
{
|
||||||
|
auto pipe = TRY(make_shared<Pipe>());
|
||||||
|
|
||||||
|
auto writer = TRY(make_shared<PipeWriter>());
|
||||||
|
auto reader = TRY(make_shared<PipeReader>());
|
||||||
|
|
||||||
|
auto auth = Scheduler::current()->auth;
|
||||||
|
|
||||||
|
pipe->m_writer = writer.ptr();
|
||||||
|
pipe->m_reader = reader.ptr();
|
||||||
|
|
||||||
|
writer->m_pipe = reader->m_pipe = pipe;
|
||||||
|
writer->chown(auth.euid, auth.egid);
|
||||||
|
reader->chown(auth.euid, auth.egid);
|
||||||
|
|
||||||
|
rpipe = reader;
|
||||||
|
wpipe = writer;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> Pipe::read(u8* buf, usize, usize length)
|
||||||
|
{
|
||||||
|
if (length > m_data_buffer.size()) length = m_data_buffer.size();
|
||||||
|
|
||||||
|
memcpy(buf, m_data_buffer.data(), length);
|
||||||
|
|
||||||
|
memmove(m_data_buffer.data(), m_data_buffer.data() + length, m_data_buffer.size() - length);
|
||||||
|
|
||||||
|
TRY(m_data_buffer.try_resize(m_data_buffer.size() - length));
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> Pipe::write(const u8* buf, usize, usize length)
|
||||||
|
{
|
||||||
|
if (!m_reader) return length;
|
||||||
|
|
||||||
|
u8* slice = TRY(m_data_buffer.slice_at_end(length));
|
||||||
|
memcpy(slice, buf, length);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pipe::blocking() const
|
||||||
|
{
|
||||||
|
return !m_data_buffer.size() && m_writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipeWriter::~PipeWriter()
|
||||||
|
{
|
||||||
|
m_pipe->m_writer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipeReader::~PipeReader()
|
||||||
|
{
|
||||||
|
m_pipe->m_reader = nullptr;
|
||||||
|
}
|
139
kernel/src/fs/Pipe.h
Normal file
139
kernel/src/fs/Pipe.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fs/VFS.h"
|
||||||
|
#include <luna/Buffer.h>
|
||||||
|
|
||||||
|
class PipeInodeBase;
|
||||||
|
class PipeReader;
|
||||||
|
class PipeWriter;
|
||||||
|
|
||||||
|
class Pipe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Result<void> create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& wpipe);
|
||||||
|
|
||||||
|
Result<usize> read(u8* buf, usize, usize length);
|
||||||
|
|
||||||
|
Result<usize> write(const u8* buf, usize, usize length);
|
||||||
|
|
||||||
|
bool blocking() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Buffer m_data_buffer;
|
||||||
|
PipeWriter* m_writer;
|
||||||
|
PipeReader* m_reader;
|
||||||
|
|
||||||
|
friend class PipeInodeBase;
|
||||||
|
friend class PipeReader;
|
||||||
|
friend class PipeWriter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PipeInodeBase : public VFS::FileInode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VFS::InodeType type() const override
|
||||||
|
{
|
||||||
|
return VFS::InodeType::FIFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> truncate(usize) override
|
||||||
|
{
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blocking() const override
|
||||||
|
{
|
||||||
|
return m_pipe->blocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
usize size() const override
|
||||||
|
{
|
||||||
|
return m_pipe->m_data_buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_link() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_unlink() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chmod(mode_t) override
|
||||||
|
{
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chown(u32 uid, u32 gid) override
|
||||||
|
{
|
||||||
|
m_uid = uid;
|
||||||
|
m_gid = gid;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
usize inode_number() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::FileSystem* fs() const override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~PipeInodeBase() = default;
|
||||||
|
|
||||||
|
friend class Pipe;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SharedPtr<Pipe> m_pipe;
|
||||||
|
u32 m_uid { 0 };
|
||||||
|
u32 m_gid { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class PipeWriter : public PipeInodeBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Result<usize> read(u8*, usize, usize) const override
|
||||||
|
{
|
||||||
|
// A FileDescriptor pointing to a PipeWriter should never be opened for reading.
|
||||||
|
check(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> write(const u8* buf, usize offset, usize length) override
|
||||||
|
{
|
||||||
|
return m_pipe->write(buf, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t mode() const override
|
||||||
|
{
|
||||||
|
return 0200;
|
||||||
|
}
|
||||||
|
|
||||||
|
~PipeWriter();
|
||||||
|
|
||||||
|
friend class Pipe;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PipeReader : public PipeInodeBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Result<usize> read(u8* buf, usize offset, usize length) const override
|
||||||
|
{
|
||||||
|
return m_pipe->read(buf, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> write(const u8*, usize, usize) override
|
||||||
|
{
|
||||||
|
// A FileDescriptor pointing to a PipeReader should never be opened for writing.
|
||||||
|
check(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t mode() const override
|
||||||
|
{
|
||||||
|
return 0400;
|
||||||
|
}
|
||||||
|
|
||||||
|
~PipeReader();
|
||||||
|
|
||||||
|
friend class Pipe;
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "fs/Mount.h"
|
||||||
#include "thread/Thread.h"
|
#include "thread/Thread.h"
|
||||||
#include <bits/modes.h>
|
#include <bits/modes.h>
|
||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
@ -14,28 +15,60 @@ namespace VFS
|
|||||||
return *root_fs->root_inode();
|
return *root_fs->root_inode();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
static constexpr int MAX_SYMLINKS = 8;
|
||||||
|
|
||||||
|
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, SharedPtr<Inode> current_inode,
|
||||||
|
bool follow_last_symlink, int& symlinks_followed)
|
||||||
{
|
{
|
||||||
|
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
||||||
|
|
||||||
if (*path == '\0') return err(ENOENT);
|
if (*path == '\0') return err(ENOENT);
|
||||||
|
|
||||||
auto parser = TRY(PathParser::create(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
|
|
||||||
SharedPtr<Inode> current_inode;
|
SharedPtr<Inode> parent_inode = current_inode;
|
||||||
|
|
||||||
if (parser.is_absolute() || !working_directory) current_inode = root_fs->root_inode();
|
|
||||||
else
|
|
||||||
current_inode = working_directory;
|
|
||||||
|
|
||||||
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)) 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()))
|
||||||
|
{
|
||||||
|
auto link = TRY(current_inode->readlink());
|
||||||
|
|
||||||
|
SharedPtr<VFS::Inode> symlink_root;
|
||||||
|
|
||||||
|
if (PathParser::is_absolute(link.chars())) symlink_root = root_fs->root_inode();
|
||||||
|
else
|
||||||
|
symlink_root = parent_inode;
|
||||||
|
|
||||||
|
symlinks_followed++;
|
||||||
|
current_inode = TRY(resolve_path_impl(link.chars(), auth, symlink_root, true, symlinks_followed));
|
||||||
|
symlinks_followed--;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_inode = current_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_inode;
|
return current_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
|
||||||
|
bool follow_last_symlink)
|
||||||
|
{
|
||||||
|
SharedPtr<Inode> current_inode;
|
||||||
|
|
||||||
|
if (PathParser::is_absolute(path) || !working_directory) current_inode = root_fs->root_inode();
|
||||||
|
else
|
||||||
|
current_inode = working_directory;
|
||||||
|
|
||||||
|
int symlinks_followed = 0;
|
||||||
|
|
||||||
|
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
|
||||||
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parser = TRY(PathParser::create(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
@ -97,6 +130,8 @@ namespace VFS
|
|||||||
|
|
||||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
|
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
|
||||||
{
|
{
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
|
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
|
||||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
|
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
|
||||||
|
|
||||||
@ -105,6 +140,8 @@ namespace VFS
|
|||||||
|
|
||||||
bool can_write(SharedPtr<Inode> inode, Credentials auth)
|
bool can_write(SharedPtr<Inode> inode, Credentials auth)
|
||||||
{
|
{
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
|
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
|
||||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
|
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
|
||||||
|
|
||||||
@ -113,6 +150,8 @@ namespace VFS
|
|||||||
|
|
||||||
bool can_read(SharedPtr<Inode> inode, Credentials auth)
|
bool can_read(SharedPtr<Inode> inode, Credentials auth)
|
||||||
{
|
{
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
|
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
|
||||||
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
|
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
|
||||||
|
|
||||||
@ -128,4 +167,61 @@ namespace VFS
|
|||||||
{
|
{
|
||||||
return inode->mode() & S_ISGID;
|
return inode->mode() & S_ISGID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_seekable(SharedPtr<Inode> inode)
|
||||||
|
{
|
||||||
|
return inode->type() != InodeType::FIFO && inode->type() != InodeType::CharacterDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs)
|
||||||
|
{
|
||||||
|
root_fs = fs;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||||
|
SharedPtr<VFS::Inode> working_directory)
|
||||||
|
{
|
||||||
|
auto parser = TRY(PathParser::create(path));
|
||||||
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
auto child = TRY(parser.basename());
|
||||||
|
|
||||||
|
kdbgln("vfs: Mounting filesystem on target %s", path);
|
||||||
|
|
||||||
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
|
if (inode->is_mountpoint()) return err(EBUSY);
|
||||||
|
|
||||||
|
auto mount = TRY(MountInode::create(inode, fs));
|
||||||
|
|
||||||
|
TRY(parent_inode->replace_entry(mount, child.chars()));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
|
||||||
|
{
|
||||||
|
auto parser = TRY(PathParser::create(path));
|
||||||
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
auto child = TRY(parser.basename());
|
||||||
|
|
||||||
|
kdbgln("vfs: Unmounting filesystem on target %s", path);
|
||||||
|
|
||||||
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
|
if (!inode->is_mountpoint()) return err(EINVAL);
|
||||||
|
|
||||||
|
// There are still open file descriptors referencing files within this file system.
|
||||||
|
if (inode->fs()->handles() != 0) return err(EBUSY);
|
||||||
|
|
||||||
|
auto mount = (MountInode*)inode.ptr();
|
||||||
|
TRY(parent_inode->replace_entry(mount->source(), child.chars()));
|
||||||
|
g_mounts.remove(mount);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <bits/makedev.h>
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
#include <luna/StaticString.h>
|
#include <luna/StaticString.h>
|
||||||
#include <luna/StringView.h>
|
#include <luna/StringView.h>
|
||||||
@ -12,12 +13,52 @@ namespace VFS
|
|||||||
{
|
{
|
||||||
RegularFile,
|
RegularFile,
|
||||||
Directory,
|
Directory,
|
||||||
Device
|
CharacterDevice,
|
||||||
|
BlockDevice,
|
||||||
|
Symlink,
|
||||||
|
FIFO,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSystem;
|
|
||||||
class Inode;
|
class Inode;
|
||||||
|
|
||||||
|
class FileSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual SharedPtr<Inode> root_inode() const = 0;
|
||||||
|
|
||||||
|
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
|
||||||
|
|
||||||
|
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
|
||||||
|
|
||||||
|
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
|
||||||
|
|
||||||
|
virtual Result<SharedPtr<Inode>> create_symlink_inode(StringView link) = 0;
|
||||||
|
|
||||||
|
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
|
||||||
|
|
||||||
|
virtual u64 handles() const
|
||||||
|
{
|
||||||
|
return m_handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void add_handle()
|
||||||
|
{
|
||||||
|
m_handles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void remove_handle()
|
||||||
|
{
|
||||||
|
m_handles--;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual dev_t host_device_id() const = 0;
|
||||||
|
|
||||||
|
virtual ~FileSystem() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
u64 m_handles { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
struct DirectoryEntry
|
struct DirectoryEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -46,6 +87,8 @@ namespace VFS
|
|||||||
|
|
||||||
virtual Result<void> remove_entry(const char* name) = 0;
|
virtual Result<void> remove_entry(const char* name) = 0;
|
||||||
|
|
||||||
|
virtual Result<void> replace_entry(SharedPtr<Inode> inode, const char* name) = 0;
|
||||||
|
|
||||||
virtual usize entries() const = 0;
|
virtual usize entries() const = 0;
|
||||||
|
|
||||||
// File-specific methods
|
// File-specific methods
|
||||||
@ -57,11 +100,27 @@ namespace VFS
|
|||||||
|
|
||||||
virtual bool blocking() const = 0;
|
virtual bool blocking() const = 0;
|
||||||
|
|
||||||
|
// Symlink-specific methods
|
||||||
|
virtual Result<StringView> readlink()
|
||||||
|
{
|
||||||
|
return StringView {};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual dev_t device_id() const
|
||||||
|
{
|
||||||
|
return luna_dev_makedev(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Metadata accessors
|
// Metadata accessors
|
||||||
virtual usize size() const = 0;
|
virtual usize size() const = 0;
|
||||||
|
|
||||||
virtual mode_t mode() const = 0;
|
virtual mode_t mode() const = 0;
|
||||||
|
|
||||||
|
virtual nlink_t nlinks() const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
virtual u32 uid() const
|
virtual u32 uid() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@ -72,6 +131,11 @@ namespace VFS
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool is_mountpoint() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void did_link() = 0;
|
virtual void did_link() = 0;
|
||||||
virtual void did_unlink() = 0;
|
virtual void did_unlink() = 0;
|
||||||
|
|
||||||
@ -81,13 +145,25 @@ namespace VFS
|
|||||||
virtual Result<void> chown(u32 uid, u32 gid) = 0;
|
virtual Result<void> chown(u32 uid, u32 gid) = 0;
|
||||||
|
|
||||||
// Generic VFS-related methods
|
// Generic VFS-related methods
|
||||||
virtual FileSystem& fs() const = 0;
|
virtual FileSystem* fs() const = 0;
|
||||||
|
|
||||||
virtual ~Inode() = default;
|
virtual ~Inode() = default;
|
||||||
|
|
||||||
virtual InodeType type() const = 0;
|
virtual InodeType type() const = 0;
|
||||||
|
|
||||||
virtual usize inode_number() const = 0;
|
virtual usize inode_number() const = 0;
|
||||||
|
|
||||||
|
virtual void add_handle()
|
||||||
|
{
|
||||||
|
auto* f = fs();
|
||||||
|
if (f) f->add_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void remove_handle()
|
||||||
|
{
|
||||||
|
auto* f = fs();
|
||||||
|
if (f) f->remove_handle();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileInode : Inode
|
class FileInode : Inode
|
||||||
@ -118,6 +194,11 @@ namespace VFS
|
|||||||
return err(ENOTDIR);
|
return err(ENOTDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
|
||||||
|
{
|
||||||
|
return err(ENOTDIR);
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> remove_entry(const char*) override
|
Result<void> remove_entry(const char*) override
|
||||||
{
|
{
|
||||||
return err(ENOTDIR);
|
return err(ENOTDIR);
|
||||||
@ -169,6 +250,11 @@ namespace VFS
|
|||||||
return err(ENOTDIR);
|
return err(ENOTDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
|
||||||
|
{
|
||||||
|
return err(ENOTDIR);
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> remove_entry(const char*) override
|
Result<void> remove_entry(const char*) override
|
||||||
{
|
{
|
||||||
return err(ENOTDIR);
|
return err(ENOTDIR);
|
||||||
@ -181,30 +267,15 @@ namespace VFS
|
|||||||
|
|
||||||
InodeType type() const override
|
InodeType type() const override
|
||||||
{
|
{
|
||||||
return InodeType::Device;
|
return InodeType::CharacterDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~DeviceInode() = default;
|
virtual ~DeviceInode() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSystem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual SharedPtr<Inode> root_inode() const = 0;
|
|
||||||
|
|
||||||
virtual Result<SharedPtr<Inode>> create_file_inode() = 0;
|
|
||||||
|
|
||||||
virtual Result<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
|
|
||||||
|
|
||||||
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
|
|
||||||
|
|
||||||
virtual ~FileSystem() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern SharedPtr<FileSystem> root_fs;
|
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
|
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);
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
|
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth,
|
||||||
SharedPtr<VFS::Inode> working_directory = {});
|
SharedPtr<VFS::Inode> working_directory = {});
|
||||||
@ -220,5 +291,13 @@ namespace VFS
|
|||||||
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_seekable(SharedPtr<Inode> inode);
|
||||||
|
|
||||||
Inode& root_inode();
|
Inode& root_inode();
|
||||||
|
|
||||||
|
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
||||||
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||||
|
SharedPtr<Inode> working_directory = {});
|
||||||
|
|
||||||
|
Result<void> umount(const char* path, Credentials auth, SharedPtr<Inode> working_directory = {});
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
#include "fs/devices/ConsoleDevice.h"
|
#include "fs/devices/ConsoleDevice.h"
|
||||||
|
#include "Log.h"
|
||||||
#include "arch/Keyboard.h"
|
#include "arch/Keyboard.h"
|
||||||
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
#include "video/TextConsole.h"
|
#include "video/TextConsole.h"
|
||||||
#include <bits/termios.h>
|
#include <bits/termios.h>
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
#include <luna/Units.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
static Buffer g_console_input;
|
static Buffer g_console_input;
|
||||||
static bool g_eof { false };
|
static bool g_eof { false };
|
||||||
static bool g_echo { true };
|
static bool g_echo { true };
|
||||||
|
|
||||||
Result<SharedPtr<Device>> ConsoleDevice::create()
|
Result<void> ConsoleDevice::create()
|
||||||
{
|
{
|
||||||
return (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
|
auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, "console");
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
||||||
@ -59,7 +64,28 @@ void ConsoleDevice::did_press_key(char key)
|
|||||||
// Ctrl+D
|
// Ctrl+D
|
||||||
if (key == 'd' && (Keyboard::modifiers() & Keyboard::LeftControl))
|
if (key == 'd' && (Keyboard::modifiers() & Keyboard::LeftControl))
|
||||||
{
|
{
|
||||||
g_eof = true;
|
if (g_temp_input.size() == 0) g_eof = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'e' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||||
|
{
|
||||||
|
Scheduler::dump_state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'm' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||||
|
{
|
||||||
|
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
|
||||||
|
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
|
||||||
|
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
||||||
|
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'h' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||||
|
{
|
||||||
|
dump_heap_usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ class ConsoleDevice : public Device
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Initializer for DeviceRegistry.
|
// Initializer for DeviceRegistry.
|
||||||
static Result<SharedPtr<Device>> create();
|
static Result<void> create();
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override;
|
Result<usize> read(u8*, usize, usize) const override;
|
||||||
|
|
||||||
|
@ -18,6 +18,11 @@ class Device
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool is_block_device() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool blocking() const = 0;
|
virtual bool blocking() const = 0;
|
||||||
|
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
@ -1,53 +1,85 @@
|
|||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "fs/VFS.h"
|
||||||
#include "fs/devices/ConsoleDevice.h"
|
#include "fs/devices/ConsoleDevice.h"
|
||||||
#include "fs/devices/FramebufferDevice.h"
|
#include "fs/devices/FramebufferDevice.h"
|
||||||
|
#include "fs/devices/FullDevice.h"
|
||||||
#include "fs/devices/NullDevice.h"
|
#include "fs/devices/NullDevice.h"
|
||||||
#include "fs/devices/ZeroDevice.h"
|
#include "fs/devices/ZeroDevice.h"
|
||||||
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
|
#include "thread/Thread.h"
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
struct DeviceDescriptor
|
struct DeviceDescriptor
|
||||||
{
|
{
|
||||||
device_create_func_t initializer;
|
SharedPtr<Device> device;
|
||||||
u32 major;
|
u32 major;
|
||||||
u32 minor;
|
u32 minor;
|
||||||
|
const char* name;
|
||||||
|
mode_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<DeviceDescriptor> g_available_devices = {};
|
Vector<DeviceDescriptor> g_available_devices = {};
|
||||||
|
|
||||||
|
SharedPtr<VFS::FileSystem> g_device_fs;
|
||||||
|
|
||||||
namespace DeviceRegistry
|
namespace DeviceRegistry
|
||||||
{
|
{
|
||||||
Result<SharedPtr<Device>> create_special_device(u32 major, u32 minor)
|
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor)
|
||||||
{
|
{
|
||||||
for (const auto& descriptor : g_available_devices)
|
for (const auto& descriptor : g_available_devices)
|
||||||
{
|
{
|
||||||
if (descriptor.major == major && descriptor.minor == minor) return descriptor.initializer();
|
if (descriptor.major == major && descriptor.minor == minor) return descriptor.device;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err(ENODEV);
|
return err(ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> register_special_device(u32 major, u32 minor, device_create_func_t initializer)
|
Result<void> create_special_device_inode(DeviceDescriptor& descriptor)
|
||||||
|
{
|
||||||
|
auto inode = TRY(g_device_fs->create_device_inode(descriptor.major, descriptor.minor));
|
||||||
|
inode->chmod(descriptor.mode);
|
||||||
|
TRY(g_device_fs->root_inode()->add_entry(inode, descriptor.name));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name, mode_t mode)
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
kdbgln("DeviceRegistry: registered new device type %u:%u", major, minor);
|
kdbgln("DeviceRegistry: registered new device type %u:%u at path /dev/%s", major, minor, name);
|
||||||
|
|
||||||
return g_available_devices.try_append(
|
auto desc = DeviceDescriptor { .device = device, .major = major, .minor = minor, .name = name, .mode = mode };
|
||||||
DeviceDescriptor { .initializer = initializer, .major = major, .minor = minor });
|
|
||||||
|
TRY(g_available_devices.try_append(desc));
|
||||||
|
|
||||||
|
TRY(create_special_device_inode(desc));
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> init()
|
Result<void> init()
|
||||||
{
|
{
|
||||||
// Hardcode default devices.
|
auto device_fs = TRY(TmpFS::FileSystem::create());
|
||||||
TRY(register_special_device(DeviceMajorTypes::Memory, 0, NullDevice::create));
|
TRY(VFS::mount("/dev", device_fs, Credentials {}));
|
||||||
TRY(register_special_device(DeviceMajorTypes::Memory, 1, ZeroDevice::create));
|
g_device_fs = device_fs;
|
||||||
TRY(register_special_device(DeviceMajorTypes::Console, 0, ConsoleDevice::create));
|
|
||||||
TRY(register_special_device(DeviceMajorTypes::Framebuffer, 0, FramebufferDevice::create));
|
NullDevice::create();
|
||||||
|
ZeroDevice::create();
|
||||||
|
FullDevice::create();
|
||||||
|
ConsoleDevice::create();
|
||||||
|
FramebufferDevice::create();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev_t next_null_device_id()
|
||||||
|
{
|
||||||
|
static u32 next_minor = 0;
|
||||||
|
return luna_dev_makedev(0, next_minor++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "fs/devices/Device.h"
|
#include "fs/devices/Device.h"
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
|
#include <sys/types.h>
|
||||||
typedef Result<SharedPtr<Device>> (*device_create_func_t)(void);
|
|
||||||
|
|
||||||
namespace DeviceRegistry
|
namespace DeviceRegistry
|
||||||
{
|
{
|
||||||
@ -15,9 +14,13 @@ namespace DeviceRegistry
|
|||||||
Framebuffer = 3,
|
Framebuffer = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<SharedPtr<Device>> create_special_device(u32 major, u32 minor);
|
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
||||||
|
|
||||||
Result<void> register_special_device(u32 major, u32 minor, device_create_func_t initializer);
|
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name,
|
||||||
|
mode_t mode = 0666);
|
||||||
|
|
||||||
Result<void> init();
|
Result<void> init();
|
||||||
|
|
||||||
|
// Used for file systems (like tmpfs) that do not have a host device.
|
||||||
|
dev_t next_null_device_id();
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
#include <bits/ioctl-defs.h>
|
#include <bits/ioctl-defs.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
|
||||||
Result<SharedPtr<Device>> FramebufferDevice::create()
|
Result<void> FramebufferDevice::create()
|
||||||
{
|
{
|
||||||
return (SharedPtr<Device>)TRY(make_shared<FramebufferDevice>());
|
auto device = (SharedPtr<Device>)TRY(make_shared<FramebufferDevice>());
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device, "fb0", 0600);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> FramebufferDevice::read(u8*, usize, usize) const
|
Result<usize> FramebufferDevice::read(u8*, usize, usize) const
|
||||||
|
@ -5,7 +5,7 @@ class FramebufferDevice : public Device
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Initializer for DeviceRegistry.
|
// Initializer for DeviceRegistry.
|
||||||
static Result<SharedPtr<Device>> create();
|
static Result<void> create();
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override;
|
Result<usize> read(u8*, usize, usize) const override;
|
||||||
|
|
||||||
@ -13,6 +13,11 @@ class FramebufferDevice : public Device
|
|||||||
|
|
||||||
bool blocking() const override;
|
bool blocking() const override;
|
||||||
|
|
||||||
|
bool is_block_device() const override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Result<u64> ioctl(int request, void* arg) override;
|
Result<u64> ioctl(int request, void* arg) override;
|
||||||
|
|
||||||
usize size() const override;
|
usize size() const override;
|
||||||
|
7
kernel/src/fs/devices/FullDevice.cpp
Normal file
7
kernel/src/fs/devices/FullDevice.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "fs/devices/FullDevice.h"
|
||||||
|
|
||||||
|
Result<void> FullDevice::create()
|
||||||
|
{
|
||||||
|
auto device = (SharedPtr<Device>)TRY(make_shared<FullDevice>());
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 2, device, "full");
|
||||||
|
}
|
28
kernel/src/fs/devices/FullDevice.h
Normal file
28
kernel/src/fs/devices/FullDevice.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
|
|
||||||
|
class FullDevice : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Initializer for DeviceRegistry.
|
||||||
|
static Result<void> create();
|
||||||
|
|
||||||
|
Result<usize> read(u8* buf, usize, usize length) const override
|
||||||
|
{
|
||||||
|
__builtin_memset(buf, 0, length);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> write(const u8*, usize, usize) override
|
||||||
|
{
|
||||||
|
return err(ENOSPC);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blocking() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FullDevice() = default;
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
#include "fs/devices/NullDevice.h"
|
#include "fs/devices/NullDevice.h"
|
||||||
|
|
||||||
Result<SharedPtr<Device>> NullDevice::create()
|
Result<void> NullDevice::create()
|
||||||
{
|
{
|
||||||
return (SharedPtr<Device>)TRY(make_shared<NullDevice>());
|
auto device = (SharedPtr<Device>)TRY(make_shared<NullDevice>());
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device, "null");
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ class NullDevice : public Device
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Initializer for DeviceRegistry.
|
// Initializer for DeviceRegistry.
|
||||||
static Result<SharedPtr<Device>> create();
|
static Result<void> create();
|
||||||
|
|
||||||
Result<usize> read(u8*, usize, usize) const override
|
Result<usize> read(u8*, usize, usize) const override
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "fs/devices/ZeroDevice.h"
|
#include "fs/devices/ZeroDevice.h"
|
||||||
|
|
||||||
Result<SharedPtr<Device>> ZeroDevice::create()
|
Result<void> ZeroDevice::create()
|
||||||
{
|
{
|
||||||
return (SharedPtr<Device>)TRY(make_shared<ZeroDevice>());
|
auto device = (SharedPtr<Device>)TRY(make_shared<ZeroDevice>());
|
||||||
|
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device, "zero");
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ class ZeroDevice : public Device
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Initializer for DeviceRegistry.
|
// Initializer for DeviceRegistry.
|
||||||
static Result<SharedPtr<Device>> create();
|
static Result<void> create();
|
||||||
|
|
||||||
Result<usize> read(u8* buf, usize, usize length) const override
|
Result<usize> read(u8* buf, usize, usize length) const override
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
|
#include "fs/Mount.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include <luna/Alloc.h>
|
#include <luna/Alloc.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
@ -23,6 +24,15 @@ namespace TmpFS
|
|||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<VFS::Inode>> FileSystem::create_symlink_inode(StringView link)
|
||||||
|
{
|
||||||
|
SharedPtr<SymlinkInode> inode = TRY(make_shared<SymlinkInode>());
|
||||||
|
inode->set_fs(*this, {});
|
||||||
|
TRY(inode->set_link(link, {}));
|
||||||
|
inode->set_inode_number(m_next_inode_number++, {});
|
||||||
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
|
}
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
|
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
|
||||||
{
|
{
|
||||||
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
|
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
|
||||||
@ -39,16 +49,29 @@ namespace TmpFS
|
|||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
|
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
|
||||||
{
|
{
|
||||||
SharedPtr<Device> device = TRY(DeviceRegistry::create_special_device(major, minor));
|
SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));
|
||||||
|
|
||||||
SharedPtr<DeviceInode> inode = TRY(make_shared<DeviceInode>());
|
SharedPtr<DeviceInode> inode = TRY(make_shared<DeviceInode>());
|
||||||
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->set_device(device, {});
|
inode->set_device(device, {});
|
||||||
|
// FIXME: This should be queried from Device directly, but Device doesn't have an API to store and retrieve its
|
||||||
|
// device ID atm.
|
||||||
|
inode->set_device_id(luna_dev_makedev(major, minor), {});
|
||||||
|
|
||||||
return (SharedPtr<VFS::Inode>)inode;
|
return (SharedPtr<VFS::Inode>)inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileSystem::FileSystem()
|
||||||
|
{
|
||||||
|
m_host_device_id = DeviceRegistry::next_null_device_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
|
||||||
|
{
|
||||||
|
return m_root_inode->replace_entry(parent, "..");
|
||||||
|
}
|
||||||
|
|
||||||
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
|
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
|
||||||
{
|
{
|
||||||
m_root_inode = root;
|
m_root_inode = root;
|
||||||
@ -64,6 +87,20 @@ namespace TmpFS
|
|||||||
return err(ENOENT);
|
return err(ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> DirInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
|
||||||
|
{
|
||||||
|
for (auto& entry : m_entries)
|
||||||
|
{
|
||||||
|
if (!strcmp(name, entry.name.chars()))
|
||||||
|
{
|
||||||
|
entry.inode = inode;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err(ENOENT);
|
||||||
|
}
|
||||||
|
|
||||||
Option<VFS::DirectoryEntry> DirInode::get(usize index) const
|
Option<VFS::DirectoryEntry> DirInode::get(usize index) const
|
||||||
{
|
{
|
||||||
if (index >= m_entries.size()) return {};
|
if (index >= m_entries.size()) return {};
|
||||||
@ -90,6 +127,8 @@ namespace TmpFS
|
|||||||
|
|
||||||
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
|
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
|
||||||
|
|
||||||
|
if (inode->is_mountpoint()) return err(EBUSY);
|
||||||
|
|
||||||
m_entries.remove_first_matching(
|
m_entries.remove_first_matching(
|
||||||
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });
|
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <luna/Badge.h>
|
#include <luna/Badge.h>
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/StaticString.h>
|
#include <luna/StaticString.h>
|
||||||
|
#include <luna/String.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
namespace TmpFS
|
namespace TmpFS
|
||||||
@ -20,19 +21,29 @@ namespace TmpFS
|
|||||||
Result<SharedPtr<VFS::Inode>> create_file_inode() override;
|
Result<SharedPtr<VFS::Inode>> create_file_inode() override;
|
||||||
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
|
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
|
||||||
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
|
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
|
||||||
|
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView link) override;
|
||||||
|
|
||||||
|
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
|
||||||
|
|
||||||
static Result<SharedPtr<VFS::FileSystem>> create();
|
static Result<SharedPtr<VFS::FileSystem>> create();
|
||||||
|
|
||||||
|
dev_t host_device_id() const override
|
||||||
|
{
|
||||||
|
return m_host_device_id;
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~FileSystem() = default;
|
virtual ~FileSystem() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSystem() = default;
|
FileSystem();
|
||||||
|
|
||||||
void set_root(SharedPtr<VFS::Inode> root);
|
void set_root(SharedPtr<VFS::Inode> root);
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> m_root_inode;
|
SharedPtr<VFS::Inode> m_root_inode;
|
||||||
|
|
||||||
Atomic<usize> m_next_inode_number { 2 };
|
Atomic<usize> m_next_inode_number { 2 };
|
||||||
|
|
||||||
|
dev_t m_host_device_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileInode : public VFS::FileInode
|
class FileInode : public VFS::FileInode
|
||||||
@ -50,9 +61,9 @@ namespace TmpFS
|
|||||||
m_inode_number = inum;
|
m_inode_number = inum;
|
||||||
}
|
}
|
||||||
|
|
||||||
VFS::FileSystem& fs() const override
|
VFS::FileSystem* fs() const override
|
||||||
{
|
{
|
||||||
return *m_fs;
|
return m_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize inode_number() const override
|
usize inode_number() const override
|
||||||
@ -83,6 +94,11 @@ namespace TmpFS
|
|||||||
return m_gid;
|
return m_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlink_t nlinks() const override
|
||||||
|
{
|
||||||
|
return (nlink_t)m_nlinks;
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> chmod(mode_t mode) override
|
Result<void> chmod(mode_t mode) override
|
||||||
{
|
{
|
||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
@ -118,11 +134,130 @@ namespace TmpFS
|
|||||||
u32 m_nlinks { 0 };
|
u32 m_nlinks { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SymlinkInode : public VFS::FileInode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SymlinkInode() = default;
|
||||||
|
|
||||||
|
VFS::InodeType type() const override
|
||||||
|
{
|
||||||
|
return VFS::InodeType::Symlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_fs(FileSystem& fs, Badge<FileSystem>)
|
||||||
|
{
|
||||||
|
m_fs = &fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_inode_number(usize inum, Badge<FileSystem>)
|
||||||
|
{
|
||||||
|
m_inode_number = inum;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> set_link(StringView link, Badge<FileSystem>)
|
||||||
|
{
|
||||||
|
m_link = TRY(String::from_string_view(link));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::FileSystem* fs() const override
|
||||||
|
{
|
||||||
|
return m_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize inode_number() const override
|
||||||
|
{
|
||||||
|
return m_inode_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> read(u8*, usize, usize) const override
|
||||||
|
{
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> write(const u8*, usize, usize) override
|
||||||
|
{
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> truncate(usize) override
|
||||||
|
{
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize size() const override
|
||||||
|
{
|
||||||
|
return m_link.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t mode() const override
|
||||||
|
{
|
||||||
|
return 0777;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 uid() const override
|
||||||
|
{
|
||||||
|
return m_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 gid() const override
|
||||||
|
{
|
||||||
|
return m_gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlink_t nlinks() const override
|
||||||
|
{
|
||||||
|
return (nlink_t)m_nlinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chmod(mode_t) override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> chown(u32 uid, u32 gid) override
|
||||||
|
{
|
||||||
|
m_uid = uid;
|
||||||
|
m_gid = gid;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_link() override
|
||||||
|
{
|
||||||
|
m_nlinks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void did_unlink() override
|
||||||
|
{
|
||||||
|
m_nlinks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<StringView> readlink() override
|
||||||
|
{
|
||||||
|
return m_link.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SymlinkInode() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VFS::FileSystem* m_fs;
|
||||||
|
String m_link;
|
||||||
|
usize m_inode_number;
|
||||||
|
u32 m_uid { 0 };
|
||||||
|
u32 m_gid { 0 };
|
||||||
|
u32 m_nlinks { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
class DeviceInode : public VFS::DeviceInode
|
class DeviceInode : public VFS::DeviceInode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DeviceInode() = default;
|
DeviceInode() = default;
|
||||||
|
|
||||||
|
VFS::InodeType type() const override
|
||||||
|
{
|
||||||
|
return m_device->is_block_device() ? VFS::InodeType::BlockDevice : VFS::InodeType::CharacterDevice;
|
||||||
|
}
|
||||||
|
|
||||||
void set_fs(FileSystem& fs, Badge<FileSystem>)
|
void set_fs(FileSystem& fs, Badge<FileSystem>)
|
||||||
{
|
{
|
||||||
m_fs = &fs;
|
m_fs = &fs;
|
||||||
@ -138,9 +273,19 @@ namespace TmpFS
|
|||||||
m_device = device;
|
m_device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
VFS::FileSystem& fs() const override
|
void set_device_id(dev_t id, Badge<FileSystem>)
|
||||||
{
|
{
|
||||||
return *m_fs;
|
m_device_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::FileSystem* fs() const override
|
||||||
|
{
|
||||||
|
return m_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_t device_id() const override
|
||||||
|
{
|
||||||
|
return m_device_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize inode_number() const override
|
usize inode_number() const override
|
||||||
@ -194,6 +339,11 @@ namespace TmpFS
|
|||||||
return m_gid;
|
return m_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlink_t nlinks() const override
|
||||||
|
{
|
||||||
|
return (nlink_t)m_nlinks;
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> chmod(mode_t mode) override
|
Result<void> chmod(mode_t mode) override
|
||||||
{
|
{
|
||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
@ -227,6 +377,7 @@ namespace TmpFS
|
|||||||
u32 m_uid { 0 };
|
u32 m_uid { 0 };
|
||||||
u32 m_gid { 0 };
|
u32 m_gid { 0 };
|
||||||
u32 m_nlinks { 0 };
|
u32 m_nlinks { 0 };
|
||||||
|
dev_t m_device_id { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirInode : public VFS::Inode
|
class DirInode : public VFS::Inode
|
||||||
@ -305,9 +456,9 @@ namespace TmpFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
VFS::FileSystem& fs() const override
|
VFS::FileSystem* fs() const override
|
||||||
{
|
{
|
||||||
return *m_fs;
|
return m_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize inode_number() const override
|
usize inode_number() const override
|
||||||
@ -341,6 +492,7 @@ namespace TmpFS
|
|||||||
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override;
|
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override;
|
||||||
|
|
||||||
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
||||||
|
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
|
||||||
|
|
||||||
virtual ~DirInode() = default;
|
virtual ~DirInode() = default;
|
||||||
|
|
||||||
|
@ -1,22 +1,14 @@
|
|||||||
#include "ELF.h"
|
|
||||||
#include "InitRD.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include "arch/MMU.h"
|
|
||||||
#include "arch/PCI.h"
|
#include "arch/PCI.h"
|
||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "boot/Init.h"
|
#include "boot/Init.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "fs/InitRD.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "fs/tmpfs/FileSystem.h"
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
#include "memory/Heap.h"
|
|
||||||
#include "memory/KernelVM.h"
|
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include "video/Framebuffer.h"
|
|
||||||
#include "video/TextConsole.h"
|
|
||||||
#include <luna/CString.h>
|
|
||||||
#include <luna/Result.h>
|
|
||||||
#include <luna/Units.h>
|
#include <luna/Units.h>
|
||||||
|
|
||||||
extern void set_host_name(StringView);
|
extern void set_host_name(StringView);
|
||||||
@ -31,7 +23,7 @@ void reap_thread()
|
|||||||
|
|
||||||
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
||||||
|
|
||||||
kernel_sleep(250);
|
kernel_wait_for_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,27 +34,24 @@ Result<void> init()
|
|||||||
// Default hostname if nobody from userspace changes it
|
// Default hostname if nobody from userspace changes it
|
||||||
set_host_name("moon"_sv);
|
set_host_name("moon"_sv);
|
||||||
|
|
||||||
kinfoln("Current platform: %s", CPU::platform_string());
|
kinfoln("Current platform: %s", CPU::platform_string().chars());
|
||||||
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"));
|
kinfoln("Current processor: %s", CPU::identify().value_or("(unknown)"_sv).chars());
|
||||||
|
|
||||||
Timer::init();
|
|
||||||
|
|
||||||
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
|
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
|
||||||
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
|
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
|
||||||
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
||||||
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
||||||
|
|
||||||
Thread::init();
|
auto root = TRY(TmpFS::FileSystem::create());
|
||||||
Scheduler::init();
|
TRY(VFS::mount_root(root));
|
||||||
|
TRY(InitRD::populate_vfs());
|
||||||
VFS::root_fs = TRY(TmpFS::FileSystem::create());
|
|
||||||
TRY(DeviceRegistry::init());
|
TRY(DeviceRegistry::init());
|
||||||
InitRD::populate_vfs();
|
|
||||||
|
|
||||||
auto init = TRY(VFS::resolve_path("/bin/init", Credentials {}));
|
auto init = TRY(VFS::resolve_path("/bin/init", Credentials {}));
|
||||||
TRY(Scheduler::new_userspace_thread(init, "/bin/init"));
|
auto init_thread = TRY(Scheduler::new_userspace_thread(init, "/bin/init"));
|
||||||
|
|
||||||
Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
|
auto reap = Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
|
||||||
|
Scheduler::set_reap_thread(reap);
|
||||||
|
|
||||||
PCI::scan(
|
PCI::scan(
|
||||||
[](const PCI::Device& device) {
|
[](const PCI::Device& device) {
|
||||||
@ -71,43 +60,36 @@ Result<void> init()
|
|||||||
},
|
},
|
||||||
{ .klass = 1 });
|
{ .klass = 1 });
|
||||||
|
|
||||||
CPU::platform_finish_init();
|
|
||||||
|
|
||||||
// Disable console logging before transferring control to userspace.
|
// Disable console logging before transferring control to userspace.
|
||||||
setup_log(log_debug_enabled(), log_serial_enabled(), false);
|
setup_log(log_debug_enabled(), log_serial_enabled(), false);
|
||||||
|
|
||||||
CPU::enable_interrupts();
|
init_thread->wake_up();
|
||||||
|
|
||||||
return {};
|
kernel_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void init_wrapper()
|
[[noreturn]] void init_wrapper()
|
||||||
{
|
{
|
||||||
auto rc = init();
|
auto rc = init();
|
||||||
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
|
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
|
||||||
CPU::idle_loop();
|
kernel_exit();
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr u64 BOOTSTRAP_STACK_PAGES = 8;
|
|
||||||
|
|
||||||
// FIXME: Reclaim this memory as soon as we leave the init task (so as soon as the Scheduler runs a task switch)
|
|
||||||
static u64 allocate_initial_kernel_stack()
|
|
||||||
{
|
|
||||||
u64 address = MemoryManager::alloc_for_kernel(BOOTSTRAP_STACK_PAGES + 1, MMU::ReadWrite | MMU::NoExecute).value();
|
|
||||||
// First page is a guard page, the rest is stack.
|
|
||||||
MMU::unmap(address); // Unmap (without deallocating VM) one guard page so that attempts to access it fail with a
|
|
||||||
// non-present page fault.
|
|
||||||
|
|
||||||
// The actual stack.
|
|
||||||
Stack stack { address + ARCH_PAGE_SIZE, BOOTSTRAP_STACK_PAGES * ARCH_PAGE_SIZE };
|
|
||||||
|
|
||||||
return stack.top();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" [[noreturn]] void _start()
|
extern "C" [[noreturn]] void _start()
|
||||||
{
|
{
|
||||||
Init::check_magic();
|
Init::check_magic();
|
||||||
Init::early_init();
|
Init::early_init();
|
||||||
u64 bootstrap_stack_top = allocate_initial_kernel_stack();
|
|
||||||
CPU::bootstrap_switch_stack(bootstrap_stack_top, (void*)init_wrapper);
|
Timer::init();
|
||||||
|
|
||||||
|
Thread::init();
|
||||||
|
Scheduler::init();
|
||||||
|
|
||||||
|
Scheduler::new_kernel_thread(init_wrapper, "[kinit]");
|
||||||
|
|
||||||
|
CPU::platform_finish_init();
|
||||||
|
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
|
||||||
|
CPU::idle_loop();
|
||||||
}
|
}
|
||||||
|
@ -396,57 +396,25 @@ namespace MemoryManager
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate_readable_page(u64 address)
|
bool validate_page_default_access(u64 address)
|
||||||
{
|
{
|
||||||
auto rc = MMU::get_flags(address);
|
auto rc = MMU::get_flags(address);
|
||||||
if (rc.has_error()) return false;
|
if (rc.has_error()) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate_writable_page(u64 address)
|
bool validate_page_access(u64 address, int flags)
|
||||||
{
|
{
|
||||||
auto rc = MMU::get_flags(address);
|
auto rc = MMU::get_flags(address);
|
||||||
if (rc.has_error()) return false;
|
if (rc.has_error()) return false;
|
||||||
if (rc.value() & MMU::ReadWrite) return true;
|
if (rc.value() & flags) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate_user_readable_page(u64 address)
|
|
||||||
{
|
|
||||||
auto rc = MMU::get_flags(address);
|
|
||||||
if (rc.has_error()) return false;
|
|
||||||
if (rc.value() & MMU::User) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validate_user_writable_page(u64 address)
|
|
||||||
{
|
|
||||||
auto rc = MMU::get_flags(address);
|
|
||||||
if (rc.has_error()) return false;
|
|
||||||
if ((rc.value() & MMU::User) && (rc.value() && MMU::ReadWrite)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validate_userspace_string(u64 address)
|
|
||||||
{
|
|
||||||
if (!validate_user_readable_page(address)) return false;
|
|
||||||
|
|
||||||
while (*(char*)address != 0)
|
|
||||||
{
|
|
||||||
address++;
|
|
||||||
if (address % ARCH_PAGE_SIZE)
|
|
||||||
{
|
|
||||||
if (!validate_user_readable_page(address)) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Make this more efficient.
|
// FIXME: Make this more efficient.
|
||||||
Result<String> strdup_from_user(u64 address)
|
Result<String> strdup_from_user(u64 address)
|
||||||
{
|
{
|
||||||
if (!validate_user_readable_page(address)) return err(EFAULT);
|
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
|
||||||
|
|
||||||
Vector<char> result;
|
Vector<char> result;
|
||||||
|
|
||||||
@ -456,7 +424,7 @@ namespace MemoryManager
|
|||||||
address++;
|
address++;
|
||||||
if (address % ARCH_PAGE_SIZE)
|
if (address % ARCH_PAGE_SIZE)
|
||||||
{
|
{
|
||||||
if (!validate_user_readable_page(address)) return err(EFAULT);
|
if (!validate_page_access(address, MMU::User)) return err(EFAULT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,37 +433,26 @@ namespace MemoryManager
|
|||||||
return String::from_cstring(result.data());
|
return String::from_cstring(result.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate_user_write(void* user, usize size)
|
bool validate_access(const void* mem, usize size, int flags)
|
||||||
{
|
{
|
||||||
uintptr_t user_ptr = (uintptr_t)user;
|
uintptr_t address = (uintptr_t)mem;
|
||||||
uintptr_t user_page = align_down<ARCH_PAGE_SIZE>(user_ptr);
|
uintptr_t page = align_down<ARCH_PAGE_SIZE>(address);
|
||||||
|
|
||||||
uintptr_t diff = user_ptr - user_page;
|
uintptr_t diff = address - page;
|
||||||
|
|
||||||
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
|
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
|
||||||
|
|
||||||
while (pages--)
|
while (pages--)
|
||||||
{
|
{
|
||||||
if (!validate_user_writable_page(user_page)) return false;
|
if (flags > 0)
|
||||||
user_page += ARCH_PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validate_user_read(const void* user, usize size)
|
|
||||||
{
|
{
|
||||||
uintptr_t user_ptr = (uintptr_t)user;
|
if (!validate_page_access(page, flags)) return false;
|
||||||
uintptr_t user_page = align_down<ARCH_PAGE_SIZE>(user_ptr);
|
}
|
||||||
|
else
|
||||||
uintptr_t diff = user_ptr - user_page;
|
|
||||||
|
|
||||||
usize pages = get_blocks_from_size(size + diff, ARCH_PAGE_SIZE);
|
|
||||||
|
|
||||||
while (pages--)
|
|
||||||
{
|
{
|
||||||
if (!validate_user_readable_page(user_page)) return false;
|
if (!validate_page_default_access(page)) return false;
|
||||||
user_page += ARCH_PAGE_SIZE;
|
}
|
||||||
|
page += ARCH_PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -513,7 +470,7 @@ namespace MemoryManager
|
|||||||
// Userspace pointer not aligned on page boundary
|
// Userspace pointer not aligned on page boundary
|
||||||
if (user_ptr != user_page)
|
if (user_ptr != user_page)
|
||||||
{
|
{
|
||||||
if (!validate_user_writable_page(user_page)) return false;
|
if (!validate_page_access(user_page, MMU::ReadWrite | MMU::User)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (size--)
|
while (size--)
|
||||||
@ -521,7 +478,7 @@ namespace MemoryManager
|
|||||||
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
|
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
|
||||||
if (user_ptr % ARCH_PAGE_SIZE)
|
if (user_ptr % ARCH_PAGE_SIZE)
|
||||||
{
|
{
|
||||||
if (!validate_user_writable_page(user_ptr)) return false;
|
if (!validate_page_access(user_ptr, MMU::ReadWrite | MMU::User)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(u8*)user_ptr = *kernel_ptr++;
|
*(u8*)user_ptr = *kernel_ptr++;
|
||||||
@ -541,7 +498,7 @@ namespace MemoryManager
|
|||||||
// Userspace pointer not aligned on page boundary
|
// Userspace pointer not aligned on page boundary
|
||||||
if (user_ptr != user_page)
|
if (user_ptr != user_page)
|
||||||
{
|
{
|
||||||
if (!validate_user_readable_page(user_page)) return false;
|
if (!validate_page_access(user_page, MMU::User)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (size--)
|
while (size--)
|
||||||
@ -549,7 +506,7 @@ namespace MemoryManager
|
|||||||
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
|
// Crossed a page boundary, gotta check the page tables again before touching any memory!!
|
||||||
if (user_ptr % ARCH_PAGE_SIZE)
|
if (user_ptr % ARCH_PAGE_SIZE)
|
||||||
{
|
{
|
||||||
if (!validate_user_readable_page(user_ptr)) return false;
|
if (!validate_page_access(user_ptr, MMU::User)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*kernel_ptr++ = *(const u8*)user_ptr;
|
*kernel_ptr++ = *(const u8*)user_ptr;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "arch/MMU.h"
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
namespace MemoryManager
|
namespace MemoryManager
|
||||||
{
|
{
|
||||||
|
constexpr int DEFAULT_ACCESS = 0;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
Result<void> protect_kernel_sections();
|
Result<void> protect_kernel_sections();
|
||||||
@ -19,17 +22,22 @@ namespace MemoryManager
|
|||||||
Result<void> remap(u64 address, usize count, int flags);
|
Result<void> remap(u64 address, usize count, int flags);
|
||||||
Result<void> remap_unaligned(u64 address, usize count, int flags);
|
Result<void> remap_unaligned(u64 address, usize count, int flags);
|
||||||
|
|
||||||
bool validate_readable_page(u64 address);
|
bool validate_page_access(u64 address, int flags);
|
||||||
bool validate_writable_page(u64 address);
|
bool validate_page_default_access(u64 address);
|
||||||
|
|
||||||
bool validate_user_readable_page(u64 address);
|
|
||||||
bool validate_user_writable_page(u64 address);
|
|
||||||
|
|
||||||
bool validate_userspace_string(u64 address);
|
|
||||||
Result<String> strdup_from_user(u64 address);
|
Result<String> strdup_from_user(u64 address);
|
||||||
|
|
||||||
bool validate_user_write(void* user, usize size);
|
bool validate_access(const void* mem, usize size, int flags);
|
||||||
bool validate_user_read(const void* user, usize size);
|
|
||||||
|
inline bool validate_user_write(void* user, usize size)
|
||||||
|
{
|
||||||
|
return validate_access(user, size, MMU::ReadWrite | MMU::User);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool validate_user_read(const void* user, usize size)
|
||||||
|
{
|
||||||
|
return validate_access(user, size, MMU::User);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> bool validate_user_write_typed(T* user)
|
template <typename T> bool validate_user_write_typed(T* user)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,10 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));
|
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)) return err(EACCES);
|
||||||
|
|
||||||
|
inode->add_handle();
|
||||||
|
if (current->current_directory) current->current_directory->remove_handle();
|
||||||
|
|
||||||
current->current_directory = inode;
|
current->current_directory = inode;
|
||||||
current->current_directory_path = move(path);
|
current->current_directory_path = move(path);
|
||||||
@ -25,11 +29,15 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
|
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto old_wdir = current->current_directory_path.view();
|
auto old_wdir = current->current_directory_path.view();
|
||||||
|
|
||||||
String new_path = TRY(PathParser::join(old_wdir.is_empty() ? "/"_sv : old_wdir, path.view()));
|
String new_path = TRY(PathParser::join(old_wdir.is_empty() ? "/"_sv : old_wdir, path.view()));
|
||||||
|
|
||||||
|
inode->add_handle();
|
||||||
|
if (current->current_directory) current->current_directory->remove_handle();
|
||||||
|
|
||||||
current->current_directory = inode;
|
current->current_directory = inode;
|
||||||
current->current_directory_path = move(new_path);
|
current->current_directory_path = move(new_path);
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "ELF.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
|
#include "thread/ELF.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include "thread/ThreadImage.h"
|
#include "thread/ThreadImage.h"
|
||||||
#include <bits/modes.h>
|
#include <bits/modes.h>
|
||||||
@ -31,15 +31,36 @@ static Result<Vector<String>> copy_string_vector_from_userspace(u64 address)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 calculate_userspace_stack_size(const Vector<String>& v)
|
||||||
|
{
|
||||||
|
u64 total { 0 };
|
||||||
|
|
||||||
|
for (const auto& str : v)
|
||||||
|
{
|
||||||
|
// The string's byte count + a terminating NUL byte.
|
||||||
|
total += str.length() + 1;
|
||||||
|
// The pointer to said string in the userspace array.
|
||||||
|
total += sizeof(char*);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The NULL pointer at the end of the userspace array.
|
||||||
|
total += sizeof(char*);
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr usize MAX_ARGV_STACK_SIZE = 2 * ARCH_PAGE_SIZE;
|
||||||
|
|
||||||
Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
|
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
|
||||||
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
|
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
|
||||||
|
|
||||||
auto current = Scheduler::current();
|
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
|
||||||
|
return err(E2BIG);
|
||||||
|
|
||||||
// FIXME: Make sure argv & envp are not too big.
|
auto current = Scheduler::current();
|
||||||
|
|
||||||
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||||
|
|
||||||
@ -67,7 +88,11 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
auto& descriptor = current->fd_table[i];
|
auto& descriptor = current->fd_table[i];
|
||||||
if (!descriptor.has_value()) continue;
|
if (!descriptor.has_value()) continue;
|
||||||
if (descriptor->flags & O_CLOEXEC) descriptor = {};
|
if (descriptor->flags & O_CLOEXEC)
|
||||||
|
{
|
||||||
|
descriptor->inode->remove_handle();
|
||||||
|
descriptor = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MMU::delete_userspace_page_directory(current->directory);
|
MMU::delete_userspace_page_directory(current->directory);
|
||||||
@ -104,14 +129,19 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
|||||||
|
|
||||||
thread->state = ThreadState::Runnable;
|
thread->state = ThreadState::Runnable;
|
||||||
thread->is_kernel = false;
|
thread->is_kernel = false;
|
||||||
thread->parent_id = current->id;
|
|
||||||
thread->fp_data.save();
|
thread->fp_data.save();
|
||||||
thread->name = current->name;
|
thread->name = current->name;
|
||||||
thread->auth = current->auth;
|
thread->auth = current->auth;
|
||||||
thread->current_directory = current->current_directory;
|
thread->current_directory = current->current_directory;
|
||||||
thread->current_directory_path = move(current_directory_path);
|
thread->current_directory_path = move(current_directory_path);
|
||||||
|
thread->umask = current->umask;
|
||||||
|
thread->parent = current;
|
||||||
|
|
||||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
|
{
|
||||||
|
thread->fd_table[i] = current->fd_table[i];
|
||||||
|
if (current->fd_table[i].has_value()) current->fd_table[i]->inode->add_handle();
|
||||||
|
}
|
||||||
|
|
||||||
image->apply(thread);
|
image->apply(thread);
|
||||||
|
|
||||||
|
@ -7,7 +7,21 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
Scheduler::for_each_child((pid_t)current->id, [](Thread* child) { child->parent_id = 1; });
|
Scheduler::for_each_child(current, [](Thread* child) {
|
||||||
|
child->parent = Scheduler::init_thread();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* parent = current->parent;
|
||||||
|
if (parent && parent->state == ThreadState::Waiting)
|
||||||
|
{
|
||||||
|
auto child = *parent->child_being_waited_for;
|
||||||
|
if (child == -1 || child == (pid_t)current->id)
|
||||||
|
{
|
||||||
|
parent->child_being_waited_for = (pid_t)current->id;
|
||||||
|
parent->wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
current->status = status;
|
current->status = status;
|
||||||
current->state = ThreadState::Exited;
|
current->state = ThreadState::Exited;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "fs/Pipe.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
@ -32,7 +33,7 @@ Result<u64> sys_read(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
usize nread = TRY(descriptor.inode->read(buf, descriptor.offset, size));
|
usize nread = TRY(descriptor.inode->read(buf, descriptor.offset, size));
|
||||||
|
|
||||||
descriptor.offset += nread;
|
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nread;
|
||||||
|
|
||||||
return nread;
|
return nread;
|
||||||
}
|
}
|
||||||
@ -51,11 +52,11 @@ Result<u64> sys_write(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (!descriptor.is_writable()) return err(EBADF);
|
if (!descriptor.is_writable()) return err(EBADF);
|
||||||
|
|
||||||
if (descriptor.should_append()) descriptor.offset = descriptor.inode->size();
|
if (descriptor.should_append() && VFS::is_seekable(descriptor.inode)) descriptor.offset = descriptor.inode->size();
|
||||||
|
|
||||||
usize nwritten = TRY(descriptor.inode->write(buf, descriptor.offset, size));
|
usize nwritten = TRY(descriptor.inode->write(buf, descriptor.offset, size));
|
||||||
|
|
||||||
descriptor.offset += nwritten;
|
if (VFS::is_seekable(descriptor.inode)) descriptor.offset += nwritten;
|
||||||
|
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
@ -70,6 +71,10 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto& descriptor = *TRY(current->resolve_fd(fd));
|
auto& descriptor = *TRY(current->resolve_fd(fd));
|
||||||
|
|
||||||
|
if (descriptor.inode->type() == VFS::InodeType::FIFO) return err(ESPIPE);
|
||||||
|
|
||||||
|
if (!VFS::is_seekable(descriptor.inode)) return descriptor.offset;
|
||||||
|
|
||||||
off_t new_offset;
|
off_t new_offset;
|
||||||
|
|
||||||
switch (whence)
|
switch (whence)
|
||||||
@ -147,3 +152,58 @@ Result<u64> sys_ioctl(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
return descriptor.inode->ioctl(request, arg);
|
return descriptor.inode->ioctl(request, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_dup2(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int oldfd = (int)args[0];
|
||||||
|
int newfd = (int)args[1];
|
||||||
|
|
||||||
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
|
if (newfd < 0 || newfd >= FD_MAX) return err(EBADF);
|
||||||
|
|
||||||
|
auto descriptor = *TRY(current->resolve_fd(oldfd));
|
||||||
|
|
||||||
|
if (newfd == oldfd) return (u64)newfd;
|
||||||
|
|
||||||
|
current->fd_table[newfd] = descriptor;
|
||||||
|
current->fd_table[newfd]->flags &= ~O_CLOEXEC;
|
||||||
|
|
||||||
|
return (u64)newfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_pipe(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int* pfds = (int*)args[0];
|
||||||
|
|
||||||
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
|
int rfd = TRY(current->allocate_fd(0));
|
||||||
|
int wfd = TRY(current->allocate_fd(rfd + 1));
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_to_user_typed(pfds, &rfd)) return err(EFAULT);
|
||||||
|
if (!MemoryManager::copy_to_user_typed(pfds + 1, &wfd)) return err(EFAULT);
|
||||||
|
|
||||||
|
SharedPtr<VFS::Inode> rpipe;
|
||||||
|
SharedPtr<VFS::Inode> wpipe;
|
||||||
|
|
||||||
|
TRY(Pipe::create(rpipe, wpipe));
|
||||||
|
|
||||||
|
current->fd_table[rfd] = FileDescriptor { rpipe, 0, O_RDONLY };
|
||||||
|
current->fd_table[wfd] = FileDescriptor { wpipe, 0, O_WRONLY };
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_umask(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
mode_t new_umask = (mode_t)args[0];
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
mode_t old_umask = current->umask;
|
||||||
|
|
||||||
|
current->umask = new_umask & 0777;
|
||||||
|
|
||||||
|
return old_umask;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
|
#include <bits/atfile.h>
|
||||||
|
|
||||||
Result<u64> sys_getpid(Registers*, SyscallArgs)
|
Result<u64> sys_getpid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
@ -9,7 +10,9 @@ Result<u64> sys_getpid(Registers*, SyscallArgs)
|
|||||||
|
|
||||||
Result<u64> sys_getppid(Registers*, SyscallArgs)
|
Result<u64> sys_getppid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
return Scheduler::current()->parent_id;
|
auto* parent = Scheduler::current()->parent;
|
||||||
|
|
||||||
|
return parent ? parent->id : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getuid(Registers*, SyscallArgs)
|
Result<u64> sys_getuid(Registers*, SyscallArgs)
|
||||||
@ -92,33 +95,37 @@ Result<u64> sys_setegid(Registers*, SyscallArgs args)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_chmod(Registers*, SyscallArgs args)
|
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
int dirfd = (int)args[0];
|
||||||
mode_t mode = (mode_t)args[1];
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
mode_t mode = (mode_t)args[2];
|
||||||
|
int flags = (int)args[3];
|
||||||
|
|
||||||
Credentials& auth = Scheduler::current()->auth;
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto inode = TRY(VFS::resolve_path(path.chars(), auth, Scheduler::current()->current_directory));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
if (auth.euid != 0 && auth.euid != inode->uid()) return err(EPERM);
|
if (current->auth.euid != 0 && current->auth.euid != inode->uid()) return err(EPERM);
|
||||||
|
|
||||||
TRY(inode->chmod(mode));
|
TRY(inode->chmod(mode));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_chown(Registers*, SyscallArgs args)
|
Result<u64> sys_fchownat(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
int dirfd = (int)args[0];
|
||||||
u32 uid = (u32)args[1];
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
u32 gid = (u32)args[2];
|
u32 uid = (u32)args[2];
|
||||||
|
u32 gid = (u32)args[3];
|
||||||
|
int flags = (int)args[4];
|
||||||
|
|
||||||
Credentials& auth = Scheduler::current()->auth;
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto inode = TRY(VFS::resolve_path(path.chars(), auth, Scheduler::current()->current_directory));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
if (auth.euid != 0) return err(EPERM);
|
if (current->auth.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
TRY(inode->chown(uid == (u32)-1 ? inode->uid() : uid, gid == (u32)-1 ? inode->gid() : gid));
|
TRY(inode->chown(uid == (u32)-1 ? inode->uid() : uid, gid == (u32)-1 ? inode->gid() : gid));
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
#include "Log.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
|
#include <bits/atfile.h>
|
||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
|
|
||||||
Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
||||||
@ -18,7 +20,9 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (basename.view() == ".") return err(EINVAL);
|
if (basename.view() == ".") return err(EINVAL);
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false));
|
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));
|
||||||
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
if (flags > 0)
|
if (flags > 0)
|
||||||
@ -31,3 +35,90 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
|
int dirfd = (int)args[1];
|
||||||
|
auto linkpath = TRY(MemoryManager::strdup_from_user(args[2]));
|
||||||
|
|
||||||
|
if (target.is_empty()) return err(ENOENT);
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
auto parser = TRY(PathParser::create(linkpath.chars()));
|
||||||
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
|
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
||||||
|
|
||||||
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
|
TRY(VFS::validate_filename(child_name.view()));
|
||||||
|
|
||||||
|
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
|
||||||
|
TRY(inode->chown(current->auth.euid, current->auth.egid));
|
||||||
|
TRY(parent_inode->add_entry(inode, child_name.chars()));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_readlinkat(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int dirfd = (int)args[0];
|
||||||
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
char* buf = (char*)args[2];
|
||||||
|
usize bufsiz = (usize)args[3];
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false));
|
||||||
|
|
||||||
|
if (symlink->type() != VFS::InodeType::Symlink) return err(EINVAL);
|
||||||
|
|
||||||
|
auto linkpath = TRY(symlink->readlink());
|
||||||
|
check(!linkpath.is_empty());
|
||||||
|
|
||||||
|
usize nread = linkpath.length();
|
||||||
|
if (nread > bufsiz) nread = bufsiz;
|
||||||
|
|
||||||
|
kdbgln("readlink: reading %zu bytes from symlink (%s)", nread, linkpath.chars());
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_to_user(buf, linkpath.chars(), nread)) return err(EFAULT);
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int olddirfd = (int)args[0];
|
||||||
|
auto oldpath = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
int newdirfd = (int)args[2];
|
||||||
|
auto newpath = TRY(MemoryManager::strdup_from_user(args[3]));
|
||||||
|
int flags = (int)args[4];
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
auto parser = TRY(PathParser::create(newpath.chars()));
|
||||||
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
|
// FIXME: Use AT_SYMLINK_FOLLOW.
|
||||||
|
auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false));
|
||||||
|
|
||||||
|
if (target->type() == VFS::InodeType::Directory) return err(EPERM);
|
||||||
|
|
||||||
|
auto parent_inode = TRY(current->resolve_atfile(newdirfd, parent, false, true));
|
||||||
|
|
||||||
|
if (target->fs() != parent_inode->fs()) return err(EXDEV);
|
||||||
|
|
||||||
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
|
TRY(VFS::validate_filename(child_name.view()));
|
||||||
|
|
||||||
|
TRY(parent_inode->add_entry(target, child_name.chars()));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ Result<u64> sys_mkdir(Registers*, SyscallArgs args)
|
|||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
auto inode = TRY(VFS::create_directory(path.chars(), current->auth, current->current_directory));
|
auto inode = TRY(VFS::create_directory(path.chars(), current->auth, current->current_directory));
|
||||||
inode->chmod(mode);
|
inode->chmod(mode & ~current->umask);
|
||||||
inode->chown(current->auth.euid, current->auth.egid);
|
inode->chown(current->auth.euid, current->auth.egid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
#include "Log.h"
|
|
||||||
#include "fs/VFS.h"
|
|
||||||
#include "memory/MemoryManager.h"
|
|
||||||
#include "sys/Syscall.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include <bits/makedev.h>
|
|
||||||
#include <luna/PathParser.h>
|
|
||||||
|
|
||||||
Result<u64> sys_mknod(Registers*, SyscallArgs args)
|
|
||||||
{
|
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
|
||||||
mode_t mode = (mode_t)args[1];
|
|
||||||
dev_t dev = (dev_t)args[2];
|
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
|
||||||
|
|
||||||
u32 maj = luna_dev_major(dev);
|
|
||||||
u32 min = luna_dev_minor(dev);
|
|
||||||
|
|
||||||
auto parser = TRY(PathParser::create(path.chars()));
|
|
||||||
|
|
||||||
auto dirname = TRY(parser.dirname());
|
|
||||||
auto basename = TRY(parser.basename());
|
|
||||||
|
|
||||||
TRY(VFS::validate_filename(basename.view()));
|
|
||||||
|
|
||||||
auto parent = TRY(VFS::resolve_path(dirname.chars(), current->auth, current->current_directory));
|
|
||||||
if (!VFS::can_write(parent, current->auth)) return err(EACCES);
|
|
||||||
|
|
||||||
auto inode = TRY(parent->fs().create_device_inode(maj, min));
|
|
||||||
TRY(parent->add_entry(inode, basename.chars()));
|
|
||||||
|
|
||||||
TRY(inode->chmod(mode));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
34
kernel/src/sys/mount.cpp
Normal file
34
kernel/src/sys/mount.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "fs/VFS.h"
|
||||||
|
#include "fs/tmpfs/FileSystem.h"
|
||||||
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "sys/Syscall.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
|
||||||
|
Result<u64> sys_mount(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
|
auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
if (current->auth.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
|
// Right now we only support one file system.
|
||||||
|
if (fstype.view() != "tmpfs") return err(ENODEV);
|
||||||
|
|
||||||
|
auto fs = TRY(TmpFS::FileSystem::create());
|
||||||
|
TRY(VFS::mount(target.chars(), fs, current->auth, current->current_directory));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_umount(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
if (current->auth.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
|
TRY(VFS::umount(target.chars(), current->auth, current->current_directory));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -23,17 +23,22 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
// Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR
|
// Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR
|
||||||
if ((flags & O_RDWR) == 0) { return err(EINVAL); }
|
if ((flags & O_RDWR) == 0) { return err(EINVAL); }
|
||||||
|
|
||||||
if ((flags & O_DIRECTORY) & (flags & O_CREAT)) return err(EINVAL);
|
if (flags & O_TMPFILE)
|
||||||
|
{
|
||||||
|
if (!(flags & O_WRONLY)) return err(EINVAL);
|
||||||
|
if (flags & O_CREAT) return err(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
SharedPtr<VFS::Inode> parent_inode;
|
SharedPtr<VFS::Inode> parent_inode;
|
||||||
bool ok = current->resolve_atfile(dirfd, path, false, &parent_inode).try_set_value_or_error(inode, error);
|
bool ok = current->resolve_atfile(dirfd, path, false, !(flags & O_NOFOLLOW), &parent_inode)
|
||||||
|
.try_set_value_or_error(inode, error);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
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(), current->auth, parent_inode));
|
inode = TRY(VFS::create_file(path.chars(), current->auth, parent_inode));
|
||||||
inode->chmod(mode);
|
inode->chmod(mode & ~current->umask);
|
||||||
inode->chown(current->auth.euid, current->auth.egid);
|
inode->chown(current->auth.euid, current->auth.egid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -47,14 +52,32 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
|
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should only be possible if O_NOFOLLOW was in flags.
|
||||||
|
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
|
||||||
|
|
||||||
|
if (flags & O_TMPFILE)
|
||||||
|
{
|
||||||
|
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);
|
||||||
|
inode = TRY(inode->fs()->create_file_inode());
|
||||||
|
inode->chmod(mode);
|
||||||
|
inode->chown(current->auth.euid, current->auth.egid);
|
||||||
|
}
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR);
|
||||||
|
|
||||||
|
if (inode->type() == VFS::InodeType::Directory)
|
||||||
|
{
|
||||||
|
if ((flags & O_WRONLY) || (flags & O_CREAT)) return err(EISDIR);
|
||||||
|
}
|
||||||
|
|
||||||
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
|
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
|
||||||
|
|
||||||
int fd = TRY(current->allocate_fd(0));
|
int fd = TRY(current->allocate_fd(0));
|
||||||
|
|
||||||
kinfoln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
|
kinfoln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
|
||||||
|
|
||||||
|
inode->add_handle();
|
||||||
|
|
||||||
current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP };
|
current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP };
|
||||||
|
|
||||||
return (u64)fd;
|
return (u64)fd;
|
||||||
@ -71,6 +94,8 @@ Result<u64> sys_close(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (!descriptor.has_value()) return err(EBADF);
|
if (!descriptor.has_value()) return err(EBADF);
|
||||||
|
|
||||||
|
descriptor->inode->remove_handle();
|
||||||
|
|
||||||
descriptor = {};
|
descriptor = {};
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
41
kernel/src/sys/pstat.cpp
Normal file
41
kernel/src/sys/pstat.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "sys/Syscall.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
#include <bits/pstat.h>
|
||||||
|
|
||||||
|
static void set_timespec(struct timespec& ts, u64 ticks)
|
||||||
|
{
|
||||||
|
ts.tv_sec = ticks / 1000;
|
||||||
|
ts.tv_nsec = (ticks % 1000) * 1000 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_pstat(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
pid_t pid = (pid_t)args[0];
|
||||||
|
struct process* ps = (struct process*)args[1];
|
||||||
|
|
||||||
|
// If pid == -1, return the PID of the last spawned thread.
|
||||||
|
if (pid == -1) return g_threads.expect_last()->id;
|
||||||
|
|
||||||
|
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
|
struct process proc;
|
||||||
|
proc.ps_pid = (pid_t)thread->id;
|
||||||
|
proc.ps_ppid = thread->parent ? (pid_t)thread->parent->id : 0;
|
||||||
|
proc.ps_uid = thread->auth.uid;
|
||||||
|
proc.ps_gid = thread->auth.gid;
|
||||||
|
proc.ps_euid = thread->auth.euid;
|
||||||
|
proc.ps_egid = thread->auth.egid;
|
||||||
|
proc.ps_state = (int)thread->state;
|
||||||
|
proc.ps_flags = thread->is_kernel ? PS_FLAG_KRNL : 0;
|
||||||
|
set_timespec(proc.ps_time, thread->user_ticks_self + thread->kernel_ticks_self);
|
||||||
|
set_timespec(proc.ps_ktime, thread->kernel_ticks_self);
|
||||||
|
set_timespec(proc.ps_utime, thread->kernel_ticks_children);
|
||||||
|
strlcpy(proc.ps_name, thread->name.chars(), sizeof(proc.ps_name));
|
||||||
|
strlcpy(proc.ps_cwd, thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars(),
|
||||||
|
sizeof(proc.ps_cwd));
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_to_user_typed(ps, &proc)) return err(EFAULT);
|
||||||
|
|
||||||
|
return (u64)pid;
|
||||||
|
}
|
32
kernel/src/sys/resource.cpp
Normal file
32
kernel/src/sys/resource.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "sys/Syscall.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
#include <bits/rusage.h>
|
||||||
|
|
||||||
|
static void ticks_to_rusage(struct rusage* ru, u64 sticks, u64 uticks)
|
||||||
|
{
|
||||||
|
ru->ru_stime.tv_sec = sticks / 1000;
|
||||||
|
ru->ru_stime.tv_usec = (sticks % 1000) * 1000;
|
||||||
|
ru->ru_utime.tv_sec = uticks / 1000;
|
||||||
|
ru->ru_utime.tv_usec = (uticks % 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_getrusage(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int who = (int)args[0];
|
||||||
|
struct rusage* ru = (struct rusage*)args[1];
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
struct rusage kru;
|
||||||
|
switch (who)
|
||||||
|
{
|
||||||
|
case RUSAGE_SELF: ticks_to_rusage(&kru, current->kernel_ticks_self, current->user_ticks_self); break;
|
||||||
|
case RUSAGE_CHILDREN: ticks_to_rusage(&kru, current->kernel_ticks_children, current->user_ticks_children); break;
|
||||||
|
default: return err(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_to_user_typed(ru, &kru)) return err(EFAULT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
|
#include <bits/access.h>
|
||||||
#include <bits/atfile.h>
|
#include <bits/atfile.h>
|
||||||
#include <bits/modes.h>
|
#include <bits/modes.h>
|
||||||
#include <bits/struct_stat.h>
|
#include <bits/struct_stat.h>
|
||||||
@ -13,7 +14,10 @@ static mode_t make_mode(mode_t mode, VFS::InodeType type)
|
|||||||
{
|
{
|
||||||
case VFS::InodeType::RegularFile: result |= S_IFREG; break;
|
case VFS::InodeType::RegularFile: result |= S_IFREG; break;
|
||||||
case VFS::InodeType::Directory: result |= S_IFDIR; break;
|
case VFS::InodeType::Directory: result |= S_IFDIR; break;
|
||||||
case VFS::InodeType::Device: result |= S_IFCHR; break;
|
case VFS::InodeType::CharacterDevice: result |= S_IFCHR; break;
|
||||||
|
case VFS::InodeType::BlockDevice: result |= S_IFBLK; break;
|
||||||
|
case VFS::InodeType::Symlink: result |= S_IFLNK; break;
|
||||||
|
case VFS::InodeType::FIFO: result |= S_IFIFO; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,18 +33,50 @@ Result<u64> sys_fstatat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
stat kstat;
|
stat kstat;
|
||||||
|
|
||||||
kstat.st_ino = inode->inode_number();
|
kstat.st_ino = inode->inode_number();
|
||||||
kstat.st_mode = make_mode(inode->mode(), inode->type());
|
kstat.st_mode = make_mode(inode->mode(), inode->type());
|
||||||
kstat.st_nlink = 1; // FIXME: Count hard links to files.
|
kstat.st_nlink = inode->nlinks();
|
||||||
kstat.st_uid = inode->uid();
|
kstat.st_uid = inode->uid();
|
||||||
kstat.st_gid = inode->gid();
|
kstat.st_gid = inode->gid();
|
||||||
kstat.st_size = inode->size();
|
kstat.st_size = inode->size();
|
||||||
|
kstat.st_dev = inode->fs() ? inode->fs()->host_device_id() : 0;
|
||||||
|
kstat.st_rdev = inode->device_id();
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user_typed(st, &kstat)) return err(EFAULT);
|
if (!MemoryManager::copy_to_user_typed(st, &kstat)) return err(EFAULT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> sys_faccessat(Registers*, SyscallArgs args)
|
||||||
|
{
|
||||||
|
int dirfd = (int)args[0];
|
||||||
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
int amode = (int)args[2];
|
||||||
|
int flags = (int)args[3];
|
||||||
|
|
||||||
|
Credentials creds;
|
||||||
|
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
|
if (flags & AT_EACCESS) creds = current->auth;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto auth = current->auth;
|
||||||
|
creds.euid = auth.uid;
|
||||||
|
creds.egid = auth.gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
|
||||||
|
|
||||||
|
if ((amode & R_OK) && !VFS::can_read(inode, creds)) return err(EACCES);
|
||||||
|
if ((amode & W_OK) && !VFS::can_write(inode, creds)) return err(EACCES);
|
||||||
|
if ((amode & X_OK) && !VFS::can_execute(inode, creds)) return err(EACCES);
|
||||||
|
|
||||||
|
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
|
||||||
|
// otherwise.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -27,7 +27,7 @@ Result<u64> sys_uname(Registers*, SyscallArgs args)
|
|||||||
// FIXME: Hardcode this at build time instead of in code (should be the short commit hash).
|
// FIXME: Hardcode this at build time instead of in code (should be the short commit hash).
|
||||||
strncpy(result.version, "alpha", _UTSNAME_LENGTH);
|
strncpy(result.version, "alpha", _UTSNAME_LENGTH);
|
||||||
|
|
||||||
strncpy(result.machine, CPU::platform_string(), _UTSNAME_LENGTH);
|
strncpy(result.machine, CPU::platform_string().chars(), _UTSNAME_LENGTH);
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user_typed(buf, &result)) return err(EFAULT);
|
if (!MemoryManager::copy_to_user_typed(buf, &result)) return err(EFAULT);
|
||||||
|
|
||||||
|
@ -17,35 +17,44 @@ Result<u64> sys_waitpid(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
if (thread->parent_id != current->id) return err(ECHILD);
|
if (thread->parent && thread->parent != current) return err(ECHILD);
|
||||||
|
|
||||||
while (thread->state != ThreadState::Exited)
|
|
||||||
{
|
|
||||||
if (options & WNOHANG) return err(EAGAIN);
|
if (options & WNOHANG) return err(EAGAIN);
|
||||||
kernel_sleep(10);
|
|
||||||
}
|
if (thread->state != ThreadState::Exited) kernel_wait(pid);
|
||||||
|
check(thread->state == ThreadState::Exited);
|
||||||
}
|
}
|
||||||
else if (pid == -1)
|
else if (pid == -1)
|
||||||
{
|
{
|
||||||
if (!Scheduler::has_children((pid_t)current->id)) return err(ECHILD);
|
if (!Scheduler::has_children(current)) return err(ECHILD);
|
||||||
|
|
||||||
Option<Thread*> child;
|
auto child = Scheduler::find_exited_child(current);
|
||||||
|
if (!child.has_value())
|
||||||
while (child = Scheduler::find_exited_child((pid_t)current->id), !child.has_value())
|
|
||||||
{
|
{
|
||||||
if (options & WNOHANG) return err(EAGAIN);
|
if (options & WNOHANG) return err(EAGAIN);
|
||||||
kernel_sleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
kernel_wait(pid);
|
||||||
|
check(current->child_being_waited_for.value_or(-1) != -1);
|
||||||
|
|
||||||
|
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(*current->child_being_waited_for), ESRCH));
|
||||||
|
check(thread->state == ThreadState::Exited);
|
||||||
|
}
|
||||||
|
else
|
||||||
thread = child.value();
|
thread = child.value();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return err(ENOTSUP);
|
return err(ENOTSUP);
|
||||||
|
|
||||||
|
current->child_being_waited_for = {};
|
||||||
|
|
||||||
int status = (int)thread->status;
|
int status = (int)thread->status;
|
||||||
u64 id = thread->id;
|
u64 id = thread->id;
|
||||||
|
|
||||||
|
current->user_ticks_children += thread->user_ticks_self;
|
||||||
|
current->kernel_ticks_children += thread->kernel_ticks_self;
|
||||||
|
|
||||||
thread->state = ThreadState::Dying;
|
thread->state = ThreadState::Dying;
|
||||||
|
Scheduler::signal_reap_thread();
|
||||||
|
|
||||||
if (status_ptr)
|
if (status_ptr)
|
||||||
if (!MemoryManager::copy_to_user_typed(status_ptr, &status)) return err(EFAULT);
|
if (!MemoryManager::copy_to_user_typed(status_ptr, &status)) return err(EFAULT);
|
||||||
|
@ -63,7 +63,7 @@ namespace ELFLoader
|
|||||||
if (elf_header.e_machine != EM_MACH)
|
if (elf_header.e_machine != EM_MACH)
|
||||||
{
|
{
|
||||||
kerrorln("Error while loading ELF: ELF object's target architecture does not match the current one (%s)",
|
kerrorln("Error while loading ELF: ELF object's target architecture does not match the current one (%s)",
|
||||||
CPU::platform_string());
|
CPU::platform_string().chars());
|
||||||
return err(ENOEXEC);
|
return err(ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
static Thread g_idle;
|
static Thread g_idle;
|
||||||
static Thread* g_current = nullptr;
|
static Thread* g_current = nullptr;
|
||||||
|
static Thread* g_init = nullptr;
|
||||||
|
static Thread* g_reap = nullptr;
|
||||||
|
|
||||||
static const usize TICKS_PER_TIMESLICE = 20;
|
static const usize TICKS_PER_TIMESLICE = 20;
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ namespace Scheduler
|
|||||||
g_idle.set_ip((u64)CPU::idle_loop);
|
g_idle.set_ip((u64)CPU::idle_loop);
|
||||||
g_idle.state = ThreadState::Idle;
|
g_idle.state = ThreadState::Idle;
|
||||||
g_idle.is_kernel = true;
|
g_idle.is_kernel = true;
|
||||||
g_idle.parent_id = 0;
|
g_idle.parent = nullptr;
|
||||||
g_idle.name = "[idle]";
|
g_idle.name = "[idle]";
|
||||||
|
|
||||||
g_idle.ticks_left = 1;
|
g_idle.ticks_left = 1;
|
||||||
@ -52,7 +54,22 @@ namespace Scheduler
|
|||||||
return &g_idle;
|
return &g_idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> new_kernel_thread_impl(Thread* thread, const char* name)
|
Thread* init_thread()
|
||||||
|
{
|
||||||
|
return g_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_reap_thread(Thread* thread)
|
||||||
|
{
|
||||||
|
g_reap = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_reap_thread()
|
||||||
|
{
|
||||||
|
if (g_reap) g_reap->wake_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Thread*> new_kernel_thread_impl(Thread* thread, const char* name)
|
||||||
{
|
{
|
||||||
// If anything fails, make sure to clean up.
|
// If anything fails, make sure to clean up.
|
||||||
auto guard = make_scope_guard([&] { delete thread; });
|
auto guard = make_scope_guard([&] { delete thread; });
|
||||||
@ -66,8 +83,6 @@ namespace Scheduler
|
|||||||
|
|
||||||
thread->stack = thread_stack;
|
thread->stack = thread_stack;
|
||||||
|
|
||||||
thread->parent_id = 0;
|
|
||||||
|
|
||||||
thread->name = name;
|
thread->name = name;
|
||||||
|
|
||||||
thread->is_kernel = true;
|
thread->is_kernel = true;
|
||||||
@ -76,12 +91,14 @@ namespace Scheduler
|
|||||||
|
|
||||||
g_threads.append(thread);
|
g_threads.append(thread);
|
||||||
|
|
||||||
|
thread->state = ThreadState::Runnable;
|
||||||
|
|
||||||
kinfoln("Created kernel thread: id %lu with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp());
|
kinfoln("Created kernel thread: id %lu with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp());
|
||||||
|
|
||||||
return {};
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> new_kernel_thread(u64 address, const char* name)
|
Result<Thread*> new_kernel_thread(u64 address, const char* name)
|
||||||
{
|
{
|
||||||
Thread* const thread = TRY(new_thread());
|
Thread* const thread = TRY(new_thread());
|
||||||
thread->init_regs_kernel();
|
thread->init_regs_kernel();
|
||||||
@ -90,7 +107,7 @@ namespace Scheduler
|
|||||||
return new_kernel_thread_impl(thread, name);
|
return new_kernel_thread_impl(thread, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> new_kernel_thread(void (*func)(void), const char* name)
|
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name)
|
||||||
{
|
{
|
||||||
Thread* const thread = TRY(new_thread());
|
Thread* const thread = TRY(new_thread());
|
||||||
thread->init_regs_kernel();
|
thread->init_regs_kernel();
|
||||||
@ -99,7 +116,7 @@ namespace Scheduler
|
|||||||
return new_kernel_thread_impl(thread, name);
|
return new_kernel_thread_impl(thread, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
|
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
|
||||||
{
|
{
|
||||||
Thread* const thread = TRY(new_thread());
|
Thread* const thread = TRY(new_thread());
|
||||||
thread->init_regs_kernel();
|
thread->init_regs_kernel();
|
||||||
@ -109,13 +126,14 @@ namespace Scheduler
|
|||||||
return new_kernel_thread_impl(thread, name);
|
return new_kernel_thread_impl(thread, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name)
|
Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name)
|
||||||
{
|
{
|
||||||
Thread* const thread = TRY(new_thread());
|
Thread* const thread = TRY(make<Thread>());
|
||||||
|
|
||||||
|
thread->state = ThreadState::None;
|
||||||
thread->is_kernel = false;
|
thread->is_kernel = false;
|
||||||
|
thread->id = 1;
|
||||||
thread->name = name;
|
thread->name = name;
|
||||||
thread->parent_id = 0;
|
|
||||||
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
|
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
|
||||||
|
|
||||||
Vector<String> args;
|
Vector<String> args;
|
||||||
@ -139,8 +157,9 @@ namespace Scheduler
|
|||||||
thread->sp(), thread->kernel_stack.top());
|
thread->sp(), thread->kernel_stack.top());
|
||||||
|
|
||||||
g_threads.append(thread);
|
g_threads.append(thread);
|
||||||
|
g_init = thread;
|
||||||
|
|
||||||
return {};
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_thread(Thread* thread)
|
void add_thread(Thread* thread)
|
||||||
@ -163,6 +182,11 @@ namespace Scheduler
|
|||||||
{
|
{
|
||||||
auto stack = thread->kernel_stack;
|
auto stack = thread->kernel_stack;
|
||||||
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
|
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
|
||||||
|
|
||||||
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
|
{
|
||||||
|
if (thread->fd_table[i].has_value()) thread->fd_table[i]->inode->remove_handle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory);
|
if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory);
|
||||||
@ -237,20 +261,19 @@ namespace Scheduler
|
|||||||
{
|
{
|
||||||
CPU::disable_interrupts();
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
g_current->ticks++;
|
if (is_in_kernel(regs)) g_current->kernel_ticks_self++;
|
||||||
|
|
||||||
if (is_in_kernel(regs)) g_current->ticks_in_kernel++;
|
|
||||||
else
|
else
|
||||||
g_current->ticks_in_user++;
|
g_current->user_ticks_self++;
|
||||||
|
|
||||||
g_current->ticks_left--;
|
g_current->ticks_left--;
|
||||||
|
|
||||||
g_threads.for_each([](Thread* thread) {
|
for (auto* const thread : g_threads)
|
||||||
|
{
|
||||||
if (thread->state == ThreadState::Sleeping)
|
if (thread->state == ThreadState::Sleeping)
|
||||||
{
|
{
|
||||||
if (--thread->sleep_ticks_left == 0) thread->state = ThreadState::Runnable;
|
if (--thread->sleep_ticks_left == 0) thread->wake_up();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (!g_current->ticks_left) switch_task(regs);
|
if (!g_current->ticks_left) switch_task(regs);
|
||||||
}
|
}
|
||||||
@ -272,34 +295,63 @@ namespace Scheduler
|
|||||||
|
|
||||||
Option<Thread*> find_by_pid(pid_t pid)
|
Option<Thread*> find_by_pid(pid_t pid)
|
||||||
{
|
{
|
||||||
Option<Thread*> result;
|
for (auto* const thread : g_threads)
|
||||||
|
{
|
||||||
g_threads.for_each([&](Thread* thread) {
|
if (thread->id == (u64)pid && thread->state != ThreadState::Dying) return thread;
|
||||||
if (thread->id == (u64)pid && thread->state != ThreadState::Dying) result = thread;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_children(pid_t pid)
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_children(Thread* thread)
|
||||||
{
|
{
|
||||||
bool result { false };
|
bool result { false };
|
||||||
|
|
||||||
for_each_child(pid, [&](Thread*) { result = true; });
|
for_each_child(thread, [&](Thread*) {
|
||||||
|
result = true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<Thread*> find_exited_child(pid_t pid)
|
Option<Thread*> find_exited_child(Thread* thread)
|
||||||
{
|
{
|
||||||
Option<Thread*> result;
|
Option<Thread*> result;
|
||||||
|
|
||||||
for_each_child(pid, [&](Thread* child) {
|
for_each_child(thread, [&](Thread* child) {
|
||||||
if (!result.has_value() && child->state == ThreadState::Exited) result = child;
|
if (!result.has_value() && child->state == ThreadState::Exited)
|
||||||
|
{
|
||||||
|
result = child;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_state()
|
||||||
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
|
kdbgln("--- BEGIN SCHEDULER DUMP ---");
|
||||||
|
kdbgln("current at %p, id = %zu", g_current, g_current->id);
|
||||||
|
|
||||||
|
for (const auto* thread : g_threads)
|
||||||
|
{
|
||||||
|
kdbgln("%p %c [%-20s] %4zu, parent = (%-18p,%zu), state = %d, ticks: (k:%04zu,u:%04zu), status = "
|
||||||
|
"%d, cwd = %s",
|
||||||
|
thread, thread->is_kernel ? 'k' : 'u', thread->name.chars(), thread->id, thread->parent,
|
||||||
|
thread->parent ? thread->parent->id : 0, (int)thread->state, thread->kernel_ticks_self,
|
||||||
|
thread->user_ticks_self, thread->status,
|
||||||
|
thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars());
|
||||||
|
}
|
||||||
|
|
||||||
|
kdbgln("--- END SCHEDULER DUMP ---");
|
||||||
|
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kernel_sleep(u64 ms)
|
void kernel_sleep(u64 ms)
|
||||||
@ -309,9 +361,23 @@ void kernel_sleep(u64 ms)
|
|||||||
kernel_yield();
|
kernel_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kernel_wait(pid_t pid)
|
||||||
|
{
|
||||||
|
g_current->child_being_waited_for = pid;
|
||||||
|
g_current->state = ThreadState::Waiting;
|
||||||
|
kernel_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_wait_for_event()
|
||||||
|
{
|
||||||
|
g_current->state = ThreadState::Waiting;
|
||||||
|
kernel_yield();
|
||||||
|
}
|
||||||
|
|
||||||
[[noreturn]] void kernel_exit()
|
[[noreturn]] void kernel_exit()
|
||||||
{
|
{
|
||||||
g_current->state = ThreadState::Dying;
|
g_current->state = ThreadState::Dying;
|
||||||
|
Scheduler::signal_reap_thread();
|
||||||
kernel_yield();
|
kernel_yield();
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,16 @@ namespace Scheduler
|
|||||||
|
|
||||||
Thread* current();
|
Thread* current();
|
||||||
Thread* idle();
|
Thread* idle();
|
||||||
|
Thread* init_thread();
|
||||||
|
|
||||||
Result<void> new_kernel_thread(u64 address, const char* name);
|
void set_reap_thread(Thread*);
|
||||||
Result<void> new_kernel_thread(void (*func)(void), const char* name);
|
void signal_reap_thread();
|
||||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
|
|
||||||
|
|
||||||
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);
|
Result<Thread*> new_kernel_thread(u64 address, const char* name);
|
||||||
|
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name);
|
||||||
|
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
|
||||||
|
|
||||||
|
Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);
|
||||||
|
|
||||||
void add_thread(Thread* thread);
|
void add_thread(Thread* thread);
|
||||||
|
|
||||||
@ -29,18 +33,29 @@ namespace Scheduler
|
|||||||
|
|
||||||
Option<Thread*> find_by_pid(pid_t pid);
|
Option<Thread*> find_by_pid(pid_t pid);
|
||||||
|
|
||||||
template <typename Callback> void for_each_child(pid_t pid, Callback callback)
|
template <typename Callback> void for_each_child(Thread* thread, Callback callback)
|
||||||
{
|
{
|
||||||
g_threads.for_each([&](Thread* thread) {
|
for (Thread* current = thread; current; current = g_threads.next(current).value_or(nullptr))
|
||||||
if (thread->parent_id == (u64)pid) callback(thread);
|
{
|
||||||
});
|
if (current->parent == thread)
|
||||||
|
{
|
||||||
|
bool should_continue = callback(current);
|
||||||
|
if (!should_continue) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_children(pid_t pid);
|
void dump_state();
|
||||||
|
|
||||||
Option<Thread*> find_exited_child(pid_t pid);
|
bool has_children(Thread* thread);
|
||||||
|
|
||||||
|
Option<Thread*> find_exited_child(Thread* thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void kernel_yield();
|
extern "C" void kernel_yield();
|
||||||
|
void kernel_wait(pid_t pid);
|
||||||
void kernel_sleep(u64 ms);
|
void kernel_sleep(u64 ms);
|
||||||
[[noreturn]] void kernel_exit();
|
[[noreturn]] void kernel_exit();
|
||||||
|
|
||||||
|
// Freezes the current thread until someone else calls wake_up() on this thread.
|
||||||
|
void kernel_wait_for_event();
|
||||||
|
@ -12,7 +12,7 @@ LinkedList<Thread> g_threads;
|
|||||||
|
|
||||||
void Thread::init()
|
void Thread::init()
|
||||||
{
|
{
|
||||||
g_next_id = 1;
|
g_next_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Thread*> new_thread()
|
Result<Thread*> new_thread()
|
||||||
@ -49,13 +49,15 @@ Result<FileDescriptor*> Thread::resolve_fd(int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
||||||
SharedPtr<VFS::Inode>* parent_inode)
|
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
|
||||||
{
|
{
|
||||||
if (parent_inode) *parent_inode = this->current_directory;
|
if (parent_inode) *parent_inode = this->current_directory;
|
||||||
|
|
||||||
if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this->auth);
|
if (PathParser::is_absolute(path.view()))
|
||||||
|
return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink);
|
||||||
|
|
||||||
if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this->auth, this->current_directory);
|
if (dirfd == AT_FDCWD)
|
||||||
|
return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink);
|
||||||
|
|
||||||
auto descriptor = TRY(resolve_fd(dirfd));
|
auto descriptor = TRY(resolve_fd(dirfd));
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
|||||||
|
|
||||||
if (path.is_empty() && allow_empty_path) return descriptor->inode;
|
if (path.is_empty() && allow_empty_path) return descriptor->inode;
|
||||||
|
|
||||||
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode);
|
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileDescriptor::should_append()
|
bool FileDescriptor::should_append()
|
||||||
|
@ -18,9 +18,11 @@
|
|||||||
|
|
||||||
enum class ThreadState
|
enum class ThreadState
|
||||||
{
|
{
|
||||||
|
None,
|
||||||
Idle,
|
Idle,
|
||||||
Runnable,
|
Runnable,
|
||||||
Sleeping,
|
Sleeping,
|
||||||
|
Waiting,
|
||||||
Exited,
|
Exited,
|
||||||
Dying
|
Dying
|
||||||
};
|
};
|
||||||
@ -54,13 +56,13 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
Registers regs;
|
Registers regs;
|
||||||
|
|
||||||
u64 id;
|
u64 id;
|
||||||
u64 parent_id;
|
|
||||||
|
|
||||||
Credentials auth;
|
Credentials auth;
|
||||||
|
|
||||||
u64 ticks = 0;
|
u64 user_ticks_self = 0;
|
||||||
u64 ticks_in_user = 0;
|
u64 kernel_ticks_self = 0;
|
||||||
u64 ticks_in_kernel = 0;
|
u64 user_ticks_children = 0;
|
||||||
|
u64 kernel_ticks_children = 0;
|
||||||
|
|
||||||
u64 ticks_left;
|
u64 ticks_left;
|
||||||
u64 sleep_ticks_left;
|
u64 sleep_ticks_left;
|
||||||
@ -74,6 +76,7 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
Result<int> allocate_fd(int min);
|
Result<int> allocate_fd(int min);
|
||||||
Result<FileDescriptor*> resolve_fd(int fd);
|
Result<FileDescriptor*> resolve_fd(int fd);
|
||||||
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
||||||
|
bool follow_last_symlink,
|
||||||
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
||||||
|
|
||||||
FPData fp_data;
|
FPData fp_data;
|
||||||
@ -84,11 +87,16 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
|
|
||||||
u8 status { 0 };
|
u8 status { 0 };
|
||||||
|
|
||||||
|
mode_t umask { 0 };
|
||||||
|
|
||||||
StaticString<128> name;
|
StaticString<128> name;
|
||||||
|
|
||||||
String current_directory_path = {};
|
String current_directory_path = {};
|
||||||
SharedPtr<VFS::Inode> current_directory = {};
|
SharedPtr<VFS::Inode> current_directory = {};
|
||||||
|
|
||||||
|
Thread* parent { nullptr };
|
||||||
|
Option<pid_t> child_being_waited_for = {};
|
||||||
|
|
||||||
PageDirectory* directory;
|
PageDirectory* directory;
|
||||||
|
|
||||||
bool is_idle()
|
bool is_idle()
|
||||||
@ -96,6 +104,11 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
return state == ThreadState::Idle;
|
return state == ThreadState::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wake_up()
|
||||||
|
{
|
||||||
|
state = ThreadState::Runnable;
|
||||||
|
}
|
||||||
|
|
||||||
void init_regs_kernel();
|
void init_regs_kernel();
|
||||||
void init_regs_user();
|
void init_regs_user();
|
||||||
|
|
||||||
|
@ -3,11 +3,15 @@
|
|||||||
#include "thread/Thread.h"
|
#include "thread/Thread.h"
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
|
||||||
|
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
|
||||||
|
static constexpr usize DEFAULT_USER_STACK_SIZE = DEFAULT_USER_STACK_PAGES * ARCH_PAGE_SIZE;
|
||||||
|
|
||||||
static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
|
static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
|
||||||
{
|
{
|
||||||
const u64 THREAD_STACK_BASE = 0x10000;
|
const u64 THREAD_STACK_BASE = 0x10000;
|
||||||
|
|
||||||
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, 4, MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
|
||||||
|
MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
||||||
|
|
||||||
auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); });
|
auto guard = make_scope_guard([&] { MemoryManager::unmap_owned(THREAD_STACK_BASE, 4); });
|
||||||
|
|
||||||
@ -15,7 +19,7 @@ static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack)
|
|||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
|
||||||
user_stack = { THREAD_STACK_BASE, 4 * ARCH_PAGE_SIZE };
|
user_stack = { THREAD_STACK_BASE, DEFAULT_USER_STACK_SIZE };
|
||||||
kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
|
kernel_stack = { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "boot/bootboot.h"
|
#include "boot/bootboot.h"
|
||||||
#include "video/Framebuffer.h"
|
#include "video/Framebuffer.h"
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
#include <luna/CType.h>
|
||||||
#include <luna/Format.h>
|
#include <luna/Format.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/ScopeGuard.h>
|
#include <luna/ScopeGuard.h>
|
||||||
@ -109,6 +110,7 @@ namespace TextConsole
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
|
if (_iscntrl(c)) return;
|
||||||
putwchar_at(c, g_x_position, g_y_position);
|
putwchar_at(c, g_x_position, g_y_position);
|
||||||
next_char();
|
next_char();
|
||||||
if (at_end_of_screen())
|
if (at_end_of_screen())
|
||||||
|
@ -18,11 +18,15 @@ set(SOURCES
|
|||||||
src/setjmp.cpp
|
src/setjmp.cpp
|
||||||
src/env.cpp
|
src/env.cpp
|
||||||
src/pwd.cpp
|
src/pwd.cpp
|
||||||
|
src/grp.cpp
|
||||||
src/sys/stat.cpp
|
src/sys/stat.cpp
|
||||||
src/sys/mman.cpp
|
src/sys/mman.cpp
|
||||||
src/sys/wait.cpp
|
src/sys/wait.cpp
|
||||||
src/sys/ioctl.cpp
|
src/sys/ioctl.cpp
|
||||||
src/sys/utsname.cpp
|
src/sys/utsname.cpp
|
||||||
|
src/sys/mount.cpp
|
||||||
|
src/sys/pstat.cpp
|
||||||
|
src/sys/resource.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${LUNA_ARCH} STREQUAL "x86_64")
|
if(${LUNA_ARCH} STREQUAL "x86_64")
|
||||||
|
8
libc/include/alloca.h
Normal file
8
libc/include/alloca.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/* alloca.h: Stack memory allocation. */
|
||||||
|
|
||||||
|
#ifndef _ALLOCA_H
|
||||||
|
#define _ALLOCA_H
|
||||||
|
|
||||||
|
#define alloca __builtin_alloca
|
||||||
|
|
||||||
|
#endif
|
12
libc/include/bits/access.h
Normal file
12
libc/include/bits/access.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* bits/access.h: Definitions for the access() function. */
|
||||||
|
|
||||||
|
#ifndef _BITS_ACCESS_H
|
||||||
|
#define _BITS_ACCESS_H
|
||||||
|
|
||||||
|
#define R_OK 1
|
||||||
|
#define W_OK 2
|
||||||
|
#define X_OK 4
|
||||||
|
|
||||||
|
#define F_OK 0
|
||||||
|
|
||||||
|
#endif
|
@ -6,7 +6,10 @@
|
|||||||
#define AT_FDCWD -100
|
#define AT_FDCWD -100
|
||||||
|
|
||||||
#define AT_EMPTY_PATH 1
|
#define AT_EMPTY_PATH 1
|
||||||
|
#define AT_SYMLINK_NOFOLLOW 2
|
||||||
|
|
||||||
#define AT_REMOVEDIR 1
|
#define AT_REMOVEDIR 1
|
||||||
|
|
||||||
|
#define AT_EACCESS 1
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#ifndef _BITS_ATTRS_H
|
#ifndef _BITS_ATTRS_H
|
||||||
#define _BITS_ATTRS_H
|
#define _BITS_ATTRS_H
|
||||||
|
|
||||||
#if !defined(_STDLIB_H) && !defined(_STRING_H) && !defined(_ASSERT_H) && !defined(_SETJMP_H)
|
#if !defined(_STDLIB_H) && !defined(_STRING_H) && !defined(_ASSERT_H) && !defined(_SETJMP_H) && !defined(_SYS_TIME_H)
|
||||||
#error "Never include bits/attrs.h directly; use one of the standard library headers."
|
#error "Never include bits/attrs.h directly; use one of the standard library headers."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#ifndef _BITS_FIXED_SIZE_TYPES_H
|
#ifndef _BITS_FIXED_SIZE_TYPES_H
|
||||||
#define _BITS_FIXED_SIZE_TYPES_H
|
#define _BITS_FIXED_SIZE_TYPES_H
|
||||||
|
|
||||||
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H)
|
#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H)
|
||||||
#error "Never include bits/fixed-size-types.h, use the standard <stdint.h> header instead."
|
#error "Never include bits/fixed-size-types.h, use the standard <stdint.h> header instead."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -5,12 +5,20 @@
|
|||||||
|
|
||||||
#define S_IFMT 070000
|
#define S_IFMT 070000
|
||||||
#define S_IFREG 000000
|
#define S_IFREG 000000
|
||||||
|
#define S_IFLNK 010000
|
||||||
|
#define S_IFIFO 020000
|
||||||
|
#define S_IFBLK 030000
|
||||||
#define S_IFDIR 040000
|
#define S_IFDIR 040000
|
||||||
#define S_IFCHR 050000
|
#define S_IFCHR 050000
|
||||||
|
|
||||||
#define S_ISREG(mode) ((mode)&S_IFMT == S_IFREG)
|
#define __CHECK_TYPE(mode, type) (((mode)&S_IFMT) == type)
|
||||||
#define S_ISDIR(mode) ((mode)&S_IFMT == S_IFDIR)
|
|
||||||
#define S_ISCHR(mode) ((mode)&S_IFMT == S_IFCHR)
|
#define S_ISREG(mode) __CHECK_TYPE(mode, S_IFREG)
|
||||||
|
#define S_ISDIR(mode) __CHECK_TYPE(mode, S_IFDIR)
|
||||||
|
#define S_ISCHR(mode) __CHECK_TYPE(mode, S_IFCHR)
|
||||||
|
#define S_ISBLK(mode) __CHECK_TYPE(mode, S_IFBLK)
|
||||||
|
#define S_ISLNK(mode) __CHECK_TYPE(mode, S_IFLNK)
|
||||||
|
#define S_ISFIFO(mode) __CHECK_TYPE(mode, S_IFIFO)
|
||||||
|
|
||||||
#define S_IRWXU 0700
|
#define S_IRWXU 0700
|
||||||
#define S_IRUSR 0400
|
#define S_IRUSR 0400
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#define O_NONBLOCK 64
|
#define O_NONBLOCK 64
|
||||||
#define O_CLOEXEC 128
|
#define O_CLOEXEC 128
|
||||||
#define O_DIRECTORY 256
|
#define O_DIRECTORY 256
|
||||||
|
#define O_TMPFILE 512
|
||||||
|
#define O_NOFOLLOW 1024
|
||||||
|
|
||||||
#define O_ACCMODE O_RDWR
|
#define O_ACCMODE O_RDWR
|
||||||
|
|
||||||
|
34
libc/include/bits/pstat.h
Normal file
34
libc/include/bits/pstat.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* bits/pstat.h: The process structure and flags for the pstat() system call. */
|
||||||
|
|
||||||
|
#ifndef _BITS_PSTAT_H
|
||||||
|
#define _BITS_PSTAT_H
|
||||||
|
|
||||||
|
#include <bits/timespec.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define PS_IDLE 1
|
||||||
|
#define PS_RUNNABLE 2
|
||||||
|
#define PS_SLEEPING 3
|
||||||
|
#define PS_WAITING 4
|
||||||
|
#define PS_ENDED 5
|
||||||
|
|
||||||
|
#define PS_FLAG_KRNL 1
|
||||||
|
|
||||||
|
struct process
|
||||||
|
{
|
||||||
|
pid_t ps_pid;
|
||||||
|
pid_t ps_ppid;
|
||||||
|
uid_t ps_uid;
|
||||||
|
uid_t ps_euid;
|
||||||
|
gid_t ps_gid;
|
||||||
|
gid_t ps_egid;
|
||||||
|
int ps_state;
|
||||||
|
int ps_flags;
|
||||||
|
struct timespec ps_time;
|
||||||
|
struct timespec ps_ktime;
|
||||||
|
struct timespec ps_utime;
|
||||||
|
char ps_name[129];
|
||||||
|
char ps_cwd[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
17
libc/include/bits/rusage.h
Normal file
17
libc/include/bits/rusage.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* bits/rusage.h: The rusage structure. */
|
||||||
|
|
||||||
|
#ifndef _BITS_RUSAGE_H
|
||||||
|
#define _BITS_RUSAGE_H
|
||||||
|
|
||||||
|
#include <bits/timespec.h>
|
||||||
|
|
||||||
|
struct rusage
|
||||||
|
{
|
||||||
|
struct timeval ru_utime;
|
||||||
|
struct timeval ru_stime;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RUSAGE_SELF 0
|
||||||
|
#define RUSAGE_CHILDREN 1
|
||||||
|
|
||||||
|
#endif
|
@ -10,9 +10,15 @@ struct stat
|
|||||||
ino_t st_ino;
|
ino_t st_ino;
|
||||||
mode_t st_mode;
|
mode_t st_mode;
|
||||||
nlink_t st_nlink;
|
nlink_t st_nlink;
|
||||||
|
dev_t st_dev;
|
||||||
uid_t st_uid;
|
uid_t st_uid;
|
||||||
gid_t st_gid;
|
gid_t st_gid;
|
||||||
off_t st_size;
|
off_t st_size;
|
||||||
|
dev_t st_rdev;
|
||||||
|
// FIXME: Actually fill these fields in.
|
||||||
|
time_t st_atime;
|
||||||
|
time_t st_mtime;
|
||||||
|
time_t st_ctime;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* bits/timespec.h: The timespec structure. */
|
/* bits/timespec.h: The timespec and timeval structures. */
|
||||||
|
|
||||||
#ifndef _BITS_TIMESPEC_H
|
#ifndef _BITS_TIMESPEC_H
|
||||||
#define _BITS_TIMESPEC_H
|
#define _BITS_TIMESPEC_H
|
||||||
@ -11,4 +11,10 @@ struct timespec
|
|||||||
long tv_nsec;
|
long tv_nsec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct timeval
|
||||||
|
{
|
||||||
|
time_t tv_sec;
|
||||||
|
suseconds_t tv_usec;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,8 +22,11 @@ extern "C"
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DIR* opendir(const char* path);
|
DIR* opendir(const char* path);
|
||||||
|
DIR* fdopendir(int fd);
|
||||||
struct dirent* readdir(DIR* stream);
|
struct dirent* readdir(DIR* stream);
|
||||||
int closedir(DIR* stream);
|
int closedir(DIR* stream);
|
||||||
|
int dirfd(DIR* stream);
|
||||||
|
void rewinddir(DIR* stream);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ extern "C"
|
|||||||
/* Open a file path and return a file descriptor to it. */
|
/* Open a file path and return a file descriptor to it. */
|
||||||
int open(const char* path, int flags, ...);
|
int open(const char* path, int flags, ...);
|
||||||
|
|
||||||
|
/* Open a file path relative to a file descriptor and return a file descriptor to it. */
|
||||||
|
int openat(int dirfd, const char* path, int flags, ...);
|
||||||
|
|
||||||
/* Create a file and return a file descriptor to it. */
|
/* Create a file and return a file descriptor to it. */
|
||||||
int creat(const char* path, mode_t mode);
|
int creat(const char* path, mode_t mode);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user