Compare commits

...

147 Commits

Author SHA1 Message Date
30e4ef970e
all: Minor version bump up to 0.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 19:32:59 +02:00
d572d56460
libc: Add dummy time fields to struct stat
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 12:22:06 +02:00
18115ca04a
libc: Add alloca.h
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 12:11:51 +02:00
8d90d146b2
libc: Fix including sys/time.h
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 12:06:00 +02:00
88d1da59e8
kernel+libc: Add access()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 12:04:27 +02:00
bd8aaa917f
kernel+libc+ln: Add support for userspace hard link creation
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 11:32:40 +02:00
a98e9e8009
kernel: Add /dev/full
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-27 10:42:45 +02:00
b0506bf88f
kernel: Add umask
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-26 22:27:49 +02:00
d89a823924
kernel: Return EACCES when trying to chdir to an unaccessible directory 2023-05-26 20:49:13 +02:00
cba0a23db9
kernel: Make the framebuffer a block device
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-26 20:36:35 +02:00
7cdb967730
kernel: compilation fix
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-26 20:30:39 +02:00
62e14e7580
kernel+libos+apps: Support block devices and disallow seeking character devices or pipes
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-26 20:27:47 +02:00
38fae0c97b
ls: Show file modes visually
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-26 18:22:50 +02:00
4dcee8f828
sh: Print "exit" on EOF only when the shell is in interactive mode
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-26 17:29:41 +02:00
84c1871d30
libc: Check if a shell is available if system()'s command argument is NULL 2023-05-26 17:27:37 +02:00
3a51807fa6
kernel+stat: Handle pipes correctly in stat()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-23 20:54:25 +02:00
9cdfdbc6f9
kernel: Handle unnamed pipes in stat()
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-23 20:49:26 +02:00
1094042a7d
kernel: Implement st_dev and st_rdev
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-23 20:45:24 +02:00
1a6ad11462
kernel+libc+libos+ls: Add readlink()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-23 15:42:38 +02:00
b61307e5cb
ls: Add -1 and --directory
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-23 14:53:38 +02:00
04d074538f
ls: Don't do caching now that password/group file reading is more reasonable
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 22:10:01 +02:00
2c798b8bf0
Update README to match repository description
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 22:00:52 +02:00
597aada09e
ls+stat: Handle symbolic links properly
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 21:49:25 +02:00
4ec1af5e51
apps: Add ln 2023-05-20 21:49:25 +02:00
7a8ef52408
libos: Support not following symlinks 2023-05-20 21:49:25 +02:00
cb205c851c
libc: Add symlink(), symlinkat(), and lstat() 2023-05-20 21:49:25 +02:00
67a9d130e2
kernel: Add initial support for symbolic links :D 2023-05-20 21:49:24 +02:00
b75bd4cd14
libluna: Add PathParser::has_next() 2023-05-20 16:39:18 +02:00
9bb5371e8c
libluna+libc: Add strtok_r() 2023-05-20 16:37:07 +02:00
47d505dcbb
libc: Add getline() and getdelim()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 15:36:30 +02:00
5117b410db
apps: Add time
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 12:48:25 +02:00
6de90b3c93
libc: Add getrusage() 2023-05-20 12:48:17 +02:00
01111442d3
kernel: Add the getrusage() system call 2023-05-20 12:48:07 +02:00
1fa8aeecce
libos: Allow Process::exec to take a slice of StringViews instead of Strings as arguments 2023-05-20 12:47:10 +02:00
30d1dad149
libluna: Let String::join take a vector of StringViews instead of Strings 2023-05-20 12:46:33 +02:00
1db60b5a82
sh: Print a message on exit
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 12:07:56 +02:00
a7c6163e7c
libc: Add gettimeofday() 2023-05-20 12:05:22 +02:00
dc169124cc
libc: Add fchown, fchmod, and some POSIX feature test macros
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-19 19:59:20 +02:00
3db0c4fed2
apps: Add ps
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-18 21:59:38 +02:00
1506331872
kernel+libc: Add the pstat() system call
Luna-specific, but I like it.
2023-05-18 21:48:47 +02:00
0dbfbe6395
libc+apps: Avoid calling endpwent() and endgrent() after every call to get{pw,gr}{nam,uid,gid} 2023-05-18 21:47:46 +02:00
84bed3ceb3
kernel+libc: Remove mknod()
All checks were successful
continuous-integration/drone/push Build is passing
Now that we automatically create files in /dev, mknod() is not needed anymore.
2023-05-18 19:42:14 +02:00
d7c563aebd
kernel: Add reference counting for mounts
All checks were successful
continuous-integration/drone/push Build is passing
This will make sure we cannot unmount a file system while there is another one mounted inside it.
2023-05-18 16:22:31 +02:00
3a73d49aa1
kernel: Remove a mount from the mountpoint list after unmounting it 2023-05-18 16:18:09 +02:00
8f6bd29da3
kernel: Make file system reference counting work for current directories
All checks were successful
continuous-integration/drone/push Build is passing
Also, make pipes usable again.
2023-05-17 20:37:01 +02:00
1f4c4928cc
kernel+libc+apps: Add mount and umount syscalls, libc wrappers, and utilities
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-17 20:30:15 +02:00
29174ca228
kernel/VFS: Add a way to unmount file systems 2023-05-17 19:52:26 +02:00
e7d482e78a
kernel+init: Add a VFS mount system and auto-populate the devfs 2023-05-17 19:40:37 +02:00
4d106b6b8c
libos: Add error handling to vector argument usage
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-13 13:22:49 +02:00
65d3195caa
libos: Allow ArgumentParser users to specify they want leftover arguments
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-13 13:22:10 +02:00
dc7e503342
tests: Test character formatting
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-13 12:50:10 +02:00
e481ce45d0
tests: Add way more tests to TestFormat now that formatting is fixed 2023-05-13 12:45:14 +02:00
e098a3269a
libluna: Treat a negative precision as if precision was omitted 2023-05-13 12:40:03 +02:00
38541e22e9
libluna: Fix flag shenanigans in cstyle_format
Finally, zero-pad and left-align work properly!
2023-05-13 12:39:31 +02:00
5911b052dc
libluna: Add more options to to_dynamic_unit()
All checks were successful
continuous-integration/drone/push Build is passing
Also, make the output look more like how it is on linux.
2023-05-13 12:01:09 +02:00
a5ad8e16de
ls: Add the --human-readable flag 2023-05-13 11:15:28 +02:00
300d68088b
libos: Add FileSystem::stat()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-12 23:47:20 +02:00
64d0f30bd9
kernel: Disallow write-related flags when opening a directory, not only when the directory flag is set
All checks were successful
continuous-integration/drone/push Build is passing
Also return EISDIR instead of EINVAL as that is what Dr. POSIX mandates.
2023-05-11 21:29:49 +02:00
1035b91a3d
su: Do not change directory/set variables by default, instead add a --login option
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-11 20:11:09 +02:00
a935039e78
sh: Prioritize /etc/passwd over the USER environment variable 2023-05-11 20:10:10 +02:00
18130847c1
libc: Fix some environment-related bugs 2023-05-11 20:09:46 +02:00
411c6c40cd
kernel: Add the fchmodat() and fchownat() system calls
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-11 19:49:03 +02:00
4a3a92e9d4
libc: Move chmod from unistd.h to sys/stat.h
All checks were successful
continuous-integration/drone/push Build is passing
Apparently that's where it's supposed to be.
2023-05-11 19:40:34 +02:00
4a764bc315
tests+libluna: Start testing Format.cpp
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-11 16:56:28 +02:00
e203772488
README: Update broken link to ELF loader
All checks were successful
continuous-integration/drone/push Build is passing
The ELF loader was moved into the thread/ subdirectory in 0c1d33f2ec.
2023-05-10 22:58:37 +02:00
efc6d03f23
kernel+libc: Add support for unnamed pipes
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 22:48:31 +02:00
1a2fce5316
kernel+libc: Add the O_TMPFILE open flag and the tmpfile() function
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 22:04:34 +02:00
4753e80583
kernel: Make DeviceRegistry store the created devices instead of creating them when requested
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 18:31:27 +02:00
35b1b36599
tools+tests: Add a system to run all tests at once automatically
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-07 23:04:24 +02:00
916a73ca95
kernel: Rework the timer subsystem to count in microseconds
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-07 21:51:36 +02:00
9bab4c62a1
kernel: Compilation fixes
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-07 21:42:03 +02:00
72dadc6706
kernel: Define a constant to check for default memory access
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-07 21:38:38 +02:00
b25e212880
kernel: Make a generic function for checking memory flags
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-07 21:37:01 +02:00
88180b34bd
initrd: Show default credentials in the motd
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-06 22:12:06 +02:00
b08ebdc3cb
libos: Show "Options:" label even if --help and --version are the only
options
2023-05-06 22:05:08 +02:00
cd86d1d6d0
apps+initrd: Add a login utility and make it run at startup 2023-05-06 22:03:50 +02:00
b742a08cbe
su: Set the USER and SHELL variables
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-06 12:19:54 +02:00
77560bbc3e
kernel+tools: Allow loading files with different owners + add a more dynamic install script
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-06 12:11:35 +02:00
d7fee26aa2
initrd+libc: Add /etc/group and grp.h 2023-05-06 12:01:47 +02:00
9184bbfef6
kernel: Disallow passing O_WRONLY and O_DIRECTORY at the same time
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 18:53:50 +02:00
349ba0acb1
libc: Add syscall wrappers for unlinkat() and openat()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 18:50:35 +02:00
d9d8f7cdc7
kernel: Remove unused headers from main.cpp
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-04 23:59:46 +02:00
293a992133
kernel: Add a kernel_wait_for_event() function to avoid weird calls to kernel_wait in kthreads
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-04 23:43:00 +02:00
4a7f68b989
kernel: Add a convenience wake_up() function 2023-05-04 23:35:54 +02:00
3a1c22bb93
kernel: Add keyboard combinations for debug information dumps
Ctrl+Alt+E: Dump threads
Ctrl+Alt+M: Dump memory usage
Ctrl+Alt+H: Dump heap state
2023-05-04 23:32:48 +02:00
3ed9e578c7
kernel/x86_64: Wake the IO thread up only when there is an IO event, instead of polling every 10 ms 2023-05-04 23:07:09 +02:00
96f3d29d37
kernel: Tell the reap thread to run when it's needed, instead of checking every 200 ms 2023-05-04 23:06:00 +02:00
42eb0a1d74
kernel: Freeze waitpid()'s calling thread until a child tells us it's done, instead of polling it every 10 ms 2023-05-04 23:03:31 +02:00
4616997f5b
kernel: Store a thread's parent directly instead of its parent's PID 2023-05-04 22:58:04 +02:00
58eb2d7703
libc: Print failed assertions to stderr instead of stdout
All checks were successful
continuous-integration/drone/push Build is passing
This removes two FIXMEs from the time there was no stderr.
2023-05-04 16:37:13 +02:00
44e4ca804a
kernel: Make sure argument vectors passed to execve() are not too big 2023-05-04 16:36:24 +02:00
3eb78aa5f3
libos+init: Add a new Path class to handle both file descriptors and file paths
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-03 17:37:26 +02:00
abaf24d0da
kernel/CPU: Move some stuff to StringView 2023-05-03 17:35:46 +02:00
1215c38d75
libc: Add mkdtemp() and mkstemp()
All checks were successful
continuous-integration/drone/push Build is passing
No mktemp() though, as it has race conditions and was removed in POSIX.1-2008.
2023-05-03 16:43:30 +02:00
a0b45a51de
libc: Add rand() + srand()
All checks were successful
continuous-integration/drone/push Build is passing
Very basic random number generator, but that's what rand() is.
If you want secure numbers then use arc4random() or something idk
2023-05-02 21:20:24 +02:00
d9b7e8edc0
init: Read and launch service files in order using sort()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-02 20:56:28 +02:00
3628464284
libc: Fix some naming inconsistencies 2023-05-02 20:56:28 +02:00
d1801d484c
libc: Add qsort() 2023-05-02 20:56:27 +02:00
dcd93cce3a
libluna: Add a sort() function 2023-05-02 20:56:27 +02:00
6beea7f817
ls: Avoid printing an empty line when a directory is empty
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-02 11:00:28 +02:00
0fad179485
apps+libc+libos: Remove _LUNA_SYSTEM_ERROR_EXTENSIONS and reorder headers
All checks were successful
continuous-integration/drone/push Build is passing
libluna/libos headers can now go after errno.h, so there's no reason to keep them separate.
2023-05-02 10:51:53 +02:00
052ae4902e
libluna: Remove EFIXME and make others declare error_string() and error_name()
Closes #26.
2023-05-02 10:51:53 +02:00
7058ec945a
libluna: Use a String for name and handle prefix correctly in TarStream 2023-05-02 10:51:53 +02:00
6982a8c07e
libluna: Make String::from_string_view handle non-null-terminated strings properly 2023-05-02 10:51:52 +02:00
1444cbb3df
libluna: Allow constructing a StringView from a string that might not be null-terminated 2023-05-02 10:51:52 +02:00
1d6a39c924
libluna: Make String::split() inline
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 20:05:10 +02:00
376247ba8a
libluna: Add String::from_string_view()
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 20:03:16 +02:00
53ec448e33
ls: Add the -l flag 2023-05-01 20:01:05 +02:00
2572d5b238
libc: Add wrapper for the fstatat() system call 2023-05-01 20:00:43 +02:00
458e0a87cc
libc: Fix S_IS* macros 2023-05-01 20:00:10 +02:00
48df90e636
libos+apps: Use os::*print* instead of (f)printf
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 19:32:00 +02:00
a2303665fc
libos: Add os:: print(), println(), eprint(), and eprintln() 2023-05-01 19:31:15 +02:00
df590f4e26
libluna: Add String::join() 2023-05-01 19:29:17 +02:00
7fbc644753
libc: Add clearenv
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 19:06:31 +02:00
967758d464
libc: Implement setenv() and unsetenv()
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-30 14:46:34 +02:00
ac4bbd135b
libos: Add Directory::list()
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 22:41:44 +02:00
6c5d6aaf00
libos: Make remove_tree_at use os::Directory
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 21:16:44 +02:00
3e277b5d6f
libos: Introduce os::Directory 2023-04-28 21:16:43 +02:00
8adfb6fdb9
libluna: Leave String in a valid state when moved 2023-04-28 21:16:43 +02:00
ae7c841fff
tests: Make building tests optional, although with no option to toggle them on for now
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 20:02:49 +02:00
b34f2149ee
tests: Test libluna's UTF-8 suite 2023-04-28 20:01:45 +02:00
c5a867d81c
libluna: Add wcscmp 2023-04-28 20:00:26 +02:00
15dcd6ad15
libluna: Check the whole string in Utf8StringDecoder::code_points() 2023-04-28 20:00:14 +02:00
b4a5aff071
libluna: Fix UTF-8 encoding 2023-04-28 19:59:40 +02:00
80914f0bb9
ArgumentParser: Add support for version information
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 16:33:05 +02:00
0c1d33f2ec
kernel: Move some files into subdirectories
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 15:55:06 +02:00
52ce4b28aa
Scheduler: Remove redundant check in for_each_child
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 15:22:16 +02:00
7b4cfd52cd
Scheduler: Don't search threads spawned before the current thread to find children
All checks were successful
continuous-integration/drone/push Build is passing
Children will always be spawned later (and thus have a higher PID) than their parent.
2023-04-28 15:19:01 +02:00
bcdec62f51
kernel: Allow returning early from Scheduler::for_each_child 2023-04-28 15:13:53 +02:00
f3ffdd32d4
libluna: Add support for range-based iteration to LinkedList 2023-04-28 15:12:21 +02:00
77dcfab5ef
kernel: Run the initialization process in a thread
All checks were successful
continuous-integration/drone/push Build is passing
This way we can give it more stack and also reclaim it later!
2023-04-28 13:23:07 +02:00
b1e400d795
libluna: Allow callers to optimize heap allocations by telling us they won't resize the returned memory
All checks were successful
continuous-integration/drone/push Build is passing
strdup() now does this. If someone resizes strdup()-ed memory, nothing bad will happen, they will just take a small performance hit the first time.

But I think realloc-ing strdup()-ed memory is rare, that's why I did this.
2023-04-27 17:36:25 +02:00
f0fc3ec7ca
Remove old note from README
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-26 22:49:43 +02:00
7c8f195088
base64: Rename --allow-garbage to --ignore-garbage
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-26 22:37:55 +02:00
e1ac9473a2
libluna: If allowing garbage chars while decoding Base64, skip them after the padding instead of erroring out 2023-04-26 22:32:24 +02:00
fb22e14524
tests: Add tests for Base64
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-26 21:17:09 +02:00
9d33e22ae0
apps: Add base64 2023-04-26 20:58:04 +02:00
b48d1024a8
libluna: Add Base64 encoding and decoding code 2023-04-26 20:57:48 +02:00
24f4ce9669
kernel: Only allow Ctrl+D on an empty line 2023-04-26 20:42:26 +02:00
099f6131d1
kernel: Avoid printing control characters that we don't have a special meaning for 2023-04-26 20:42:08 +02:00
36bc217056
libluna: Allow constructing a Slice from another one with a different pointer type 2023-04-26 20:41:24 +02:00
cb28e2a385
libos: Add a convenience function for opening a file or standard input 2023-04-26 20:41:03 +02:00
fb09eb97ce
libos: Add File::read_all() and File::read_all_as_string() 2023-04-26 20:34:09 +02:00
5b69ce554c
init: Add a few more configuration options for services
All checks were successful
continuous-integration/drone/push Build is passing
Notably, redirecting a service's standard streams and waiting for a service to finish before continuing boot.
2023-04-25 21:00:12 +02:00
97e9fceaa4
kernel+libc: Add dup2()
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-25 20:37:30 +02:00
188a97cf54
libc: Add execlp
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-25 20:25:51 +02:00
89fd57dea4
libc: Propagate all errors in execl() and execvpe() + add execle().
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-25 17:43:11 +02:00
185 changed files with 5242 additions and 789 deletions

1
.gitignore vendored
View File

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

View File

@ -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)

View File

@ -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).

View File

@ -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
View 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;
}

View File

@ -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;
} }

View File

@ -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);

View File

@ -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();
} }
} }

View File

@ -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;
} }

View File

@ -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);

View File

@ -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
View File

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

29
apps/ln.cpp Normal file
View 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
View 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;
}

View File

@ -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;
} }

View File

@ -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
View 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
View 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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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
View 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
View 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;
}

View File

@ -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
View File

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

3
initrd/etc/init/00-motd Normal file
View File

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

3
initrd/etc/init/99-login Normal file
View File

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

View File

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

View File

@ -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.

View File

@ -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")

View File

@ -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();
} }

View File

@ -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;
} }

View File

@ -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");

View File

@ -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));

View File

@ -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;

View File

@ -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>

View File

@ -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
View 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
View 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
View 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
View 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;
};

View File

@ -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 {};
}
} }

View File

@ -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 = {});
} }

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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++);
}
} }

View File

@ -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();
} }

View File

@ -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

View File

@ -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;

View 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");
}

View 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;
};

View File

@ -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");
} }

View File

@ -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
{ {

View File

@ -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");
} }

View File

@ -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
{ {

View File

@ -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); });

View File

@ -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;

View File

@ -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();
} }

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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;

View File

@ -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
View 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;
}

View File

@ -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
View 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;
}

View 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;
}

View File

@ -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;
}

View File

@ -27,7 +27,7 @@ Result<u64> sys_uname(Registers*, SyscallArgs args)
// FIXME: Hardcode this at build time instead of in code (should be the short commit hash). // 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);

View File

@ -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);

View File

@ -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);
} }

View File

@ -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();
} }

View File

@ -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();

View File

@ -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()

View File

@ -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();

View File

@ -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 {};

View File

@ -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())

View File

@ -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
View File

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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

@ -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