Compare commits

...

195 Commits

Author SHA1 Message Date
e029679fba
kernel: Fix negative movement in the PS/2 mouse driver
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-05 10:46:05 +02:00
15f4f7c72f
apps: Remove pivot_root
All checks were successful
continuous-integration/drone/push Build is passing
This functionality is intended to be used only by preinit, so it doesn't make sense to make it a command-line utility.
2023-09-04 13:12:55 +02:00
0002c2314c
kernel: Handle tab properly in the terminal 2023-09-04 13:12:08 +02:00
27eacac19c
kernel: Add a blinking cursor to the terminal
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-04 11:44:35 +02:00
c5e11bb6cf
apps+base+libc: Use /usr/bin paths instead of /bin everywhere 2023-09-04 11:44:10 +02:00
3c9b2c49aa
init: Fix wrong log message 2023-09-04 11:43:36 +02:00
1528c772fd
kernel: Store the full command line of a process 2023-09-04 11:43:11 +02:00
39ba4c9087
ls: Add colors to output
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-02 20:01:10 +02:00
c524dc8d58
libluna+kernel: Basic ANSI escape sequences 2023-09-02 19:35:42 +02:00
e76a91d5d0
libc+libluna: Move the scanf implementation from libc to libluna 2023-09-02 15:48:58 +02:00
cb0d5cb6a1
rm: Add the -v flag
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-02 14:46:14 +02:00
06aa7725a1
libos: Stop using syscalls directly and proxy to libc 2023-09-02 14:27:04 +02:00
64f9e9dcde
libluna: Document Check.h
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 12:27:41 +02:00
9c912ddc51
libluna: Document Heap.h 2023-08-28 12:20:42 +02:00
419604a4d2
libluna: Document Buffer
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 11:10:04 +02:00
97037b06cb
libluna: Document Ignore.h and ImplPOSIX.cpp 2023-08-27 20:50:53 +02:00
c2f173f584
libc: Call __builtin_trap() in abort() if all else fails 2023-08-27 20:49:18 +02:00
a772d92e6f
libluna: Fix initial allocation for HashTable 2023-08-27 20:48:50 +02:00
6f3ed70363
kernel+libluna: Avoid scrubbing when the memory is going to be overwritten anyway
This is the case for objects with constructors and temporary memory which is filled afterwards.
2023-08-27 20:48:33 +02:00
d48142f163
libluna: Document HashMap and HashTable 2023-08-26 20:50:12 +02:00
cbea66c533
libos+libluna: Fix misspellings of "succeeded" 2023-08-26 20:49:57 +02:00
c6d817a0fd
libluna: Document Hash.h 2023-08-26 20:31:16 +02:00
55c362eecf
libluna: Document Format
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-26 12:59:22 +02:00
9fd8b10b3f
libluna: Document CString, CType and DebugLog 2023-08-26 12:43:44 +02:00
516d6bc65e
libluna: Document CircularQueue, CPath and CRC32 2023-08-26 12:33:12 +02:00
4becb2e427
init: Log requested exits
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-24 15:28:20 +02:00
3e174337ab
init: Hostname validation 2023-08-24 15:28:08 +02:00
0f377e7289
libluna: Fix off-by-one size calculation in Base64::decode_string()
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-24 12:06:01 +02:00
798a6d63aa
libluna: Document Bitset.h 2023-08-23 14:45:53 +02:00
3f1e6fc2ff
libluna+kernel: Move luna/Syscall.h to the kernel API directory
Some checks failed
continuous-integration/drone/push Build is failing
A kernel-specific header has nothing to do in libluna.
2023-08-23 14:35:21 +02:00
b8ae61b7c7
libluna: Document Bitmap
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-23 13:50:45 +02:00
1449e966ab
libluna: Document Base64.h 2023-08-23 13:34:14 +02:00
24f9dd22ec
libluna: Document Attributes.h and Badge.h 2023-08-23 13:24:28 +02:00
b8e70996c3
libluna: Document Atomic.h 2023-08-23 13:04:47 +02:00
6058a69182
libluna: Document Alloc.h 2023-08-23 11:08:18 +02:00
e247310ded
libluna: Document Alignment.h 2023-08-23 11:04:05 +02:00
a35ca0b367
libluna+kernel: Add Bitset and use it for signals
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-23 10:51:02 +02:00
d144a818d8
kernel: Completely prevent sending signals to kernel threads
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 15:25:05 +02:00
33c1a9c92b
init: Add a configurable service directory
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 15:23:06 +02:00
65834ff491
edit: Make it more user-friendly 2023-08-22 15:17:13 +02:00
5c2718545f
base+tools: Add a welcome file and store the license inside the OS 2023-08-22 15:08:07 +02:00
e5905a33e1
init: Stop using the removed 'signal' pledge
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 13:36:33 +02:00
993e94cc76
kernel: Remove the 'signal' promise and make signal functions require stdio instead
Some checks failed
continuous-integration/drone/push Build is failing
This follows OpenBSD and makes it so stdio is sufficient for an abort() call.
2023-08-22 13:34:30 +02:00
746834e2d9
kernel: Silently ignore TTY writes in graphical mode
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 13:31:31 +02:00
ce3542e2bd
apps+base: Remove many legacy test apps
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 13:29:52 +02:00
e72bc55c6f tools: Maybe, this finally works for CI...
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 10:19:17 +00:00
2abb43d709
kernel+libos: Call Vector::try_reserve where it is appropriate
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-22 11:54:00 +02:00
70a232cfcd
libluna: Make Vector grow exponentially 2023-08-22 11:53:42 +02:00
30ff704342
libluna+libos: Install built libraries into the system root
Some checks failed
continuous-integration/drone/push Build is failing
This is less important for libluna, as it is built into libc, but is needed to link programs compiled inside Luna with libos.
2023-08-21 14:06:32 +02:00
6e69d37d62
tools: Fix building ports from git repository
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-19 19:54:37 +02:00
f2a5c9ad3f
apps: Add free
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-17 20:15:39 +02:00
95a33c484e
kernel+libc: Add a memstat() syscall
We can finally show memory usage in userspace.

This could have been done using sysfs, but I'm lazy and don't want to implement that. Maybe in the next release?
2023-08-17 20:15:32 +02:00
4a654bf093
kernel: Handle OOMs better and without deadlocking
Use a separate task to do it.

Also fix a bug where the init thread would get no kernel stack ever since 5f698b477.
2023-08-17 20:14:33 +02:00
d43590e68c
kernel: Improve Your Disk IO performance by 500% with this One Trick!
Some checks failed
continuous-integration/drone/push Build is failing
The trick being caching lol.
2023-08-16 14:54:13 +02:00
c1f4997448
all: Start calling this release 0.5.0
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-16 09:57:38 +02:00
7b88b9cea3
kernel/ext2: Replace a manually allocated buffer with Buffer
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-16 09:15:29 +02:00
54cc80f649
kernel/ATA: Replace a manually allocated buffer with Buffer
This also lets us keep it empty unless it is used, in which case we resize it (if it wasn't already resized).
2023-08-16 09:15:00 +02:00
b8f81502b8
kernel: Fix some debug messages that are not shown by default 2023-08-16 09:03:25 +02:00
e8e05159c1
libluna+kernel: Make CRC32 a class 2023-08-15 19:27:09 +02:00
49a6c39c38
libc: Implement popen() and pclose() 2023-08-15 19:21:25 +02:00
706752d6b9
kernel: Return SIGPIPE/EPIPE when writing to a pipe with no more readers 2023-08-15 19:08:37 +02:00
1e68ac7312
Finally a working toolchain?
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-15 15:40:44 +02:00
6761b3fcaf
tools: Finally a proper autoconf build?
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 11:54:11 +02:00
71d1084be2 tools: Place autoconf in the correct path
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 09:34:36 +00:00
f65deb727a
tools: Fix filename in setup-autoconf.sh
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 11:27:47 +02:00
d07f592306
tools: Fix message printing in setup-autoconf.sh
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 11:26:18 +02:00
cad0bd8c48
tools: Build autoconf if needed and use it to patch libstdc++
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 11:18:53 +02:00
db3151d93b
init: Add some missing pledges
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 11:08:19 +02:00
ba3e32917e
init: Support starting services as a separate user
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-14 10:46:45 +02:00
cfb60fad25
init: Use pledge and support init --user 2023-08-14 10:46:28 +02:00
9954fc1658
libos: Add a pledge wrapper 2023-08-14 10:45:00 +02:00
a98df9e743
kernel: Return EACCES when trying to apply execpromises to a setuid program
Closes #41.
2023-08-14 09:50:52 +02:00
e2a77bb3da
kernel+libc: Add pledge support
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-12 21:38:25 +02:00
0ae409ae22
ports: Some enhancements in make-package.sh
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-11 18:25:07 +02:00
181b4c151b
tools: Build libstdc++ 2023-08-11 18:24:38 +02:00
0c64b6e040
libc: Add some stub network-related header files 2023-08-11 18:09:45 +02:00
fb3c31907d
fix 2023-08-11 18:09:28 +02:00
52064e0317
libc+kernel: Add alarm() and getpagesize() 2023-08-11 18:09:12 +02:00
ec3c1132d2
libc: Fix constness of some socket functions 2023-08-11 18:00:15 +02:00
5ea73197ad
libluna: Add a bunch more errno definitions 2023-08-11 17:59:41 +02:00
5a1adcb2a6
libc: Add putenv 2023-08-11 17:59:04 +02:00
c4f6191e24
libc: Implement some simple stuff needed for gcc 2023-08-08 22:06:11 +02:00
39e4fbd112
libc: Provide a bunch of math functions wrapped around compiler builtins 2023-08-08 20:38:38 +02:00
32fd6889b9
ports: Add pkg-config wrapper
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 20:28:11 +02:00
c6a5a81a7a
ports: Port required libraries to build GCC
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 19:51:45 +02:00
3f55a70f6e
ports: Auto-strip binaries, remove libtool .la files, add dependencies 2023-08-08 19:46:04 +02:00
b1e164f360
libc. Add basic wchar.h 2023-08-08 19:43:23 +02:00
ed8b210639
kernel: Detect some other cases of non-DMA support
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 18:23:13 +02:00
16590dbb02
ports: Avoid using two separate directories for port packages
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 17:43:27 +02:00
919c71ff85
README: More features
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 16:19:38 +02:00
1caa2c0888
Update README.md
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 16:04:10 +02:00
8748364b7e
ports: Add a binutils port =D
All checks were successful
continuous-integration/drone/push Build is passing
I can't believe the fact that there is a working gas and ld on Luna. At least, for a hello world program anyway :)

objdump seems to have some problems with stack size in some cases, but apart from that, no crashes.

And that can easily be solved with either more stack preallocation or stack resizing in the kernel on page faults.
2023-08-08 16:00:31 +02:00
49662b6069
tools: Calculate the needed fs size dynamically 2023-08-08 15:43:20 +02:00
d96ff92461
libc: Add borrowed strtod implementation 2023-08-08 15:17:25 +02:00
bfb76b5625
kernel: Properly expose block device sizes with the new metadata API 2023-08-08 15:17:08 +02:00
8c13513bf4
libc: Add strcoll()
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 14:40:14 +02:00
37e9b25b62
apps: Add touch
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 14:34:58 +02:00
a92077d311
kernel+libc: Add all variants of utime
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 14:14:35 +02:00
1481a4736a
tmpfs: Update mtime on writes
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 13:36:25 +02:00
4195e7f206
kernel+libc+stat: Add support for file times
All checks were successful
continuous-integration/drone/push Build is passing
The modification time is not updated though.
2023-08-08 13:33:40 +02:00
159c05c064
libluna: Add max() and min()
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 12:39:03 +02:00
b63a8ff245
libluna: Move get_blocks_from_size to a new header and call it ceil_div instead
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 11:58:33 +02:00
917203ef11
kernel: Run the init function in a separate block to free everything before kernel_exit is called
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-08 10:44:18 +02:00
826be882a9
kernel: Interrupt syscalls before exiting because of a signal
Closes #40.
2023-08-08 10:41:46 +02:00
198935eb30
libc: Reset the read buffer even when read() returns an error
This fixes the same data being read multiple times if an error was returned
2023-08-08 10:39:15 +02:00
7c254e5e15
kernel: Properly check memory bounds in strdup_from_user()
All checks were successful
continuous-integration/drone/push Build is passing
Missed this one in 097353e779.
2023-08-08 10:27:19 +02:00
2e63b93e48
libos: Remove debug statements from Process::spawn()
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 22:53:37 +02:00
77ebdda2e0
libos: Add Process::spawn()
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 22:49:12 +02:00
097353e779
kernel: Properly check memory bounds while touching user memory
Before this patch, one byte of each page was being accessed without checking the page's permissions.
2023-08-07 22:49:00 +02:00
10c892d606
kernel: Allow null envp in execve()
As far as I know, this is not standard, but I'm doing this as a convenience for programs using exec() right after clearenv().
2023-08-07 22:48:21 +02:00
f45734c61d
kernel/ATA: Stop storing ATA::Drive in a separate shared pointer
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 19:22:57 +02:00
bc20e1a31b
kernel: Store FD_CLOEXEC in the file descriptor itself
All checks were successful
continuous-integration/drone/push Build is passing
Closes #39.
2023-08-03 17:47:18 +02:00
b01aa72f17
libc+init+shmem-test: Add POSIX shared memory objects
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 10:32:52 +02:00
d41fb85466
libluna/SharedPtr: Count references with separately created objects properly 2023-08-03 10:32:22 +02:00
5aa042a5f2
kernel: Add constructors to SharedMemory 2023-08-03 10:31:58 +02:00
f150425222
kernel: Create a new shared memory object if the old one was lost 2023-08-03 10:31:29 +02:00
842b212685
kernel: Set a SharedMemory object's prot value 2023-08-03 10:30:43 +02:00
641b65da0f
kernel: Take devices into account in SharedMemory::free() 2023-08-03 09:33:10 +02:00
bfcca3a220
gol: Use memory-mapped IO
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 09:26:35 +02:00
b5d146b492
kernel/mmap: Fix some parameters 2023-08-03 09:26:21 +02:00
b447c1a261
kernel: Set the shmem->inode field in query_shared_memory 2023-08-03 09:25:56 +02:00
f8e86b3b01
fix 2023-08-03 09:25:38 +02:00
d8f6af99b8
kernel: Let devices use shared memory 2023-08-03 09:25:23 +02:00
c5e24e478f
kernel+libc: Add truncate and ftruncate
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 08:47:37 +02:00
53d9f5c6fc
libc: Add msync wrapper
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 22:44:54 +02:00
84c1ac4cee
kernel: Add msync
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 22:39:07 +02:00
7293d47bf0
apps: Add shmem-test
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 22:20:05 +02:00
f66b0497cf
libc: Add support for mmap()'s new syscall format 2023-08-02 22:20:05 +02:00
2572695c8d
kernel: Support mapping shared memory using mmap() 2023-08-02 22:20:05 +02:00
9443551d71
kernel: Add shared memory 2023-08-02 22:20:05 +02:00
b4527786d4
kernel/MMU: Don't copy pages on clone and allow manipulating other page directories 2023-08-02 22:17:32 +02:00
3aaf1c5d84
apps: Rename mouse to input, read keyboard packets as well
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 17:20:26 +02:00
4794d0dfef
kernel: Add a keyboard device for graphical session 2023-08-02 17:20:13 +02:00
9c1e275f34
kernel: Make /dev/mouse read-write only for root 2023-08-02 17:19:37 +02:00
6593f9241b
libc: Add wrapper for poll() 2023-08-02 17:19:16 +02:00
df4227eab8
kernel: Make the poll() system call actually work 2023-08-02 17:18:38 +02:00
f8cb6e03df
kernel: Allow turning off the TTY by setting it to graphical mode
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 15:16:10 +02:00
207d901de8
kernel+libc: Add the poll() syscall
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 14:48:20 +02:00
df77fc8de8
libluna: Remove make_array() and destroy_array()
Placement new on arrays is a bit unreliable and could cause out-of-bounds data accesses.
2023-08-02 14:47:58 +02:00
b1fb6dee8a
login: Create a new process group to log in 2023-08-02 14:47:13 +02:00
aac8280e8a
libc+libos: Properly propagate errors through fgetc() and File::getchar()
This restores proper ^C behavior in the shell.
2023-08-02 14:46:47 +02:00
d0ceec6952
mouse: Show textual description of buttons
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 13:58:17 +02:00
7983d63b8e
libos: Add documentation for os::File::BufferingMode
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 13:15:51 +02:00
2198dedb96
apps: Add mouse
All checks were successful
continuous-integration/drone/push Build is passing
A simple mouse packet reader.
2023-08-02 11:56:12 +02:00
cd9219df52
tools: Install kernel API headers into /usr/include/moon 2023-08-02 11:56:00 +02:00
e8f3dd4cf9
kernel: Add a PS/2 mouse driver 2023-08-02 11:55:45 +02:00
6c26236167
libluna: Add CircularQueue::is_empty() 2023-08-02 11:55:08 +02:00
b17793134e
libos: Let users change the buffering mode of a File 2023-08-02 11:54:47 +02:00
dc35c42371
kernel/VFS: Rework the metadata system so that there is a single metadata struct
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-01 17:20:28 +02:00
4c87d72b44
kernel/binfmt: Add documentation + support script interpreters being scripts themselves
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-31 20:41:18 +02:00
d01087362e
libc: Set the close-on-exec flag for directories opened using opendir()
This is apparently mandated by POSIX.
2023-07-31 10:58:06 +02:00
3598dacbed
init: Remove support for 'Script' parameters
All checks were successful
continuous-integration/drone/push Build is passing
This can now be changed to good old 'Command', putting a shebang into the script,
thanks to the new shebang support in the kernel.
2023-07-30 18:47:38 +02:00
3638d3da46
kernel: Add binary format for shebang scripts 2023-07-30 18:46:19 +02:00
1c76675e40
kernel: Add a framework to add more executable formats, possibly from userspace
This lets us implement shebangs and possibly an interface similar to Linux's binfmt_misc.
2023-07-30 18:25:44 +02:00
6e269c6bc4
preinit: Search for init in several directories
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 13:08:53 +02:00
43fa128e29
Update README.md
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 11:55:20 +02:00
8475a3aad9
socket-client: Send a user-provided message to the server
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-30 11:46:53 +02:00
187f0ff83e
kernel: Rename Inode::blocking() to Inode::will_block_if_read()
All checks were successful
continuous-integration/drone/pr Build is passing
2023-07-30 11:41:53 +02:00
0c873923e8
kernel: Make OpenFileDescription shareable
All checks were successful
continuous-integration/drone/pr Build is passing
2023-07-30 11:33:46 +02:00
63745565db
apps: Add socket client
Some checks failed
continuous-integration/drone/pr Build is failing
2023-07-30 11:33:06 +02:00
7b24a4d1c6
libc: Add wrappers for listen(), connect(), and accept() 2023-07-30 11:33:06 +02:00
8d3b3aaf05
libluna: Add a few more network-related errno codes 2023-07-30 11:33:06 +02:00
bb3127c212
kernel: Implement listen(), connect() and accept() 2023-07-30 11:33:06 +02:00
cca806f088
libluna: Add a variant of CircularQueue that dynamically allocates its buffer at runtime
This is needed to implement the backlog queue for listening sockets.
2023-07-30 11:33:06 +02:00
7a7ae086f5
kernel: Use a did_close callback for UnixSockets 2023-07-30 11:33:06 +02:00
fb08594a18
kernel: Separate FileDescriptors and OpenFileDescription
Also, add a did_close() callback for OpenFileDescriptions losing all their references.
2023-07-30 11:33:06 +02:00
9303c44269
apps: Add socket-test 2023-07-30 11:33:06 +02:00
a12b018b03
kernel+libc: Add basic Unix sockets (creation and binding) 2023-07-30 11:33:06 +02:00
c4e30c3029
kernel: Add functionality to allow external inodes (such as sockets) to acquire an inode number from a file system
This is only implemented in tmpfs.
2023-07-30 11:33:06 +02:00
c1d08b904e
kernel+libluna: Add Buffer::dequeue_data() 2023-07-30 11:33:05 +02:00
200bb6c240
kernel+libc+libos: Add inode type for sockets 2023-07-30 11:33:05 +02:00
6b0bc66fd2
libluna: Add new socket-related errno codes 2023-07-30 11:33:05 +02:00
4ed7ec5e93
libluna: Store SharedPtr's ref count in the object itself
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 11:32:46 +02:00
28cc4b2306
kernel: Add default action for SIGWINCH
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-27 14:00:30 +02:00
11df5a2ec3
kernel: Use pid_t internally for process IDs
All checks were successful
continuous-integration/drone/push Build is passing
This removes a bunch of casts between pid_t and u64, and makes more sense since pid_t is literally the data type for process IDs.
2023-07-26 21:32:00 +02:00
5aad7d3154
kernel/x86_64: Fail on invalid interrupts instead of calling FIXME_UNHANDLED_INTERRUPT
All checks were successful
continuous-integration/drone/push Build is passing
A FIXME is something that should be changed later,
this is a check for something that should never happen.
2023-07-25 18:23:33 +02:00
58fa297068
libluna: Wrap around when iterating through a HashTable's buckets array
All checks were successful
continuous-integration/drone/push Build is passing
Why am I so dumb?
2023-07-25 18:19:45 +02:00
28dd8194af
kernel: Include waited-for grandchildren in RUSAGE_CHILDREN's times
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-25 17:50:15 +02:00
b88da4811f
kernel: Remove debug message in readlink
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-25 17:30:56 +02:00
7f990b161b
libluna: Fix comparison of StringViews without null termination
All checks were successful
continuous-integration/drone/push Build is passing
This regressed in de7e58c274, and made value arguments pretty much unusable.

This really needs a test...
2023-07-25 17:25:18 +02:00
9bb3fed611
libluna: Use the right unsigned integer type for wcscmp()'s return type 2023-07-25 17:23:27 +02:00
105ed79f8f
kernel: Reenable userspace stack tracing, but hidden behind a config flag
All checks were successful
continuous-integration/drone/push Build is passing
Sometimes this is needed for userspace program debugging (such as ports),
but sometimes it can crash, so we leave it off by default.
2023-07-25 17:02:09 +02:00
905e71527e
libc: Actually implement sigsetjmp() and siglongjmp() 2023-07-24 19:39:22 +02:00
c6d91c89cd
stat: Change program description
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 19:17:36 +02:00
2e2b87b714
ports+tools: Add bc port
All checks were successful
continuous-integration/drone/push Build is passing
It segfaults when writing "about", but oh well...
2023-07-24 19:14:22 +02:00
b12f42cfe2
libc: Add SIGWINCH
Not used for anything at the moment, I guess if a user program wants to send it...

Required by bc.
2023-07-24 19:11:06 +02:00
c70790bf62
ports: Add some defaults for CMake projects as well
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 18:50:51 +02:00
a990cc145e
ports: Add initial documentation to the port system
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 18:29:44 +02:00
b0d7870ec6
ports: Add minitar (with examples!)
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 17:20:54 +02:00
9a00b29304
ports: Basic ports system + nasm port
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 17:07:49 +02:00
033aff4f6c
all: Error out on bidirectional characters (CVE-2021-42574)
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 15:06:48 +02:00
c5af1bcef9
libc: Fix typo in fopen description
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 11:57:47 +02:00
b3c478f19e
init: Make the log stream line-buffered
All checks were successful
continuous-integration/drone/push Build is passing
Looks like log output stopped showing after buffering was implemented, since the log was not flushed properly.
2023-07-24 11:54:49 +02:00
ad3f3bf4db
kernel: Log the release name and build date in the boot log 2023-07-24 11:48:01 +02:00
01359af288
tools: Make the Ext2 partition bigger (32M)
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-22 16:11:34 +02:00
262 changed files with 8615 additions and 1822 deletions

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

@ -28,8 +28,6 @@ luna_app(stat.cpp stat)
luna_app(uname.cpp uname)
luna_app(base64.cpp base64)
luna_app(login.cpp login)
luna_app(ipc-test.cpp ipc-test)
luna_app(signal-test.cpp signal-test)
luna_app(mount.cpp mount)
luna_app(umount.cpp umount)
luna_app(ps.cpp ps)
@ -37,8 +35,8 @@ luna_app(time.cpp time)
luna_app(ln.cpp ln)
luna_app(mktemp.cpp mktemp)
luna_app(sysfuzz.cpp sysfuzz)
luna_app(pivot_root.cpp pivot_root)
luna_app(cp.cpp cp)
luna_app(kill.cpp kill)
luna_app(gol.cpp gol)
luna_app(buffer-test.cpp buffer-test)
luna_app(touch.cpp touch)
luna_app(free.cpp free)

View File

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

View File

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

50
apps/free.cpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
static const char* file_type(mode_t mode)
{
@ -24,7 +24,7 @@ Result<int> luna_main(int argc, char** argv)
bool follow_symlinks { false };
os::ArgumentParser parser;
parser.add_description("Display file status.");
parser.add_description("Display file metadata.");
parser.add_system_program_info("stat"_sv);
parser.add_positional_argument(path, "path", true);
parser.add_switch_argument(follow_symlinks, 'L', "dereference"_sv, "follow symlinks");
@ -36,10 +36,20 @@ Result<int> luna_main(int argc, char** argv)
char buf[11];
os::format_mode(st.st_mode, buf);
printf(" File: %s\n", path.chars());
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));
printf("Inode: %lu Links: %lu\n", st.st_ino, st.st_nlink);
printf(" Mode: (%#o/%s) UID: %u GID: %u\n", st.st_mode & ~S_IFMT, buf, st.st_uid, st.st_gid);
char atime[256];
strftime(atime, sizeof(atime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_atim.tv_sec));
char mtime[256];
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_mtim.tv_sec));
char ctime[256];
strftime(ctime, sizeof(ctime), "%Y-%m-%d %H:%M:%S", gmtime(&st.st_ctim.tv_sec));
printf(" File: %s\n", path.chars());
printf(" Size: %zu (%s)\n", st.st_size, file_type(st.st_mode));
printf(" Inode: %lu Links: %lu\n", st.st_ino, st.st_nlink);
printf(" Mode: (%#o/%s) UID: %u GID: %u\n", st.st_mode & ~S_IFMT, buf, st.st_uid, st.st_gid);
printf("Access: %s.%.9ld\n", atime, st.st_atim.tv_nsec);
printf("Modify: %s.%.9ld\n", mtime, st.st_mtim.tv_nsec);
printf("Change: %s.%.9ld\n", ctime, st.st_ctim.tv_nsec);
return 0;
}

View File

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

View File

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

47
apps/touch.cpp Normal file
View File

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

View File

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

View File

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

14
base/etc/skel/welcome Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

69
kernel/src/Pledge.cpp Normal file
View File

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

19
kernel/src/Pledge.h Normal file
View File

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

131
kernel/src/api/Keyboard.h Normal file
View File

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

22
kernel/src/api/Mouse.h Normal file
View File

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

View File

@ -7,7 +7,8 @@
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
_e(getpgid)
_e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \
_e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat)
enum Syscalls
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

154
kernel/src/binfmt/ELF.cpp Normal file
View File

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

View File

@ -1,6 +1,8 @@
#pragma once
#include "binfmt/BinaryFormat.h"
#include "fs/VFS.h"
#include "memory/AddressSpace.h"
#include <luna/SharedPtr.h>
#include <luna/Types.h>
#define ELFMAG "\177ELF"
@ -47,12 +49,20 @@ typedef struct
u64 p_align; /* Segment alignment */
} Elf64_Phdr;
struct ELFData
class ELFLoader : public BinaryFormatLoader
{
u64 entry;
};
public:
Result<bool> sniff() override;
Result<u64> load(AddressSpace* space) override;
namespace ELFLoader
{
Result<ELFData> load(SharedPtr<VFS::Inode> inode, AddressSpace* space);
Result<Vector<String>> cmdline(const String& path, Vector<String> args) override;
StringView format() const override
{
return "elf";
}
ELFLoader(SharedPtr<VFS::Inode> inode, int recursion_level);
static Result<SharedPtr<BinaryFormatLoader>> create(SharedPtr<VFS::Inode> inode, void*, int);
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,44 +37,19 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
return err(EISDIR);
}
bool blocking() const override
bool will_block_if_read() const override
{
return false;
}
usize size() const override
const VFS::InodeMetadata& metadata() const override
{
return 0;
return m_mount_root_inode->metadata();
}
mode_t mode() const override
Result<void> set_metadata(const VFS::InodeMetadata& metadata) override
{
return m_mount_root_inode->mode();
}
u32 uid() const override
{
return m_mount_root_inode->uid();
}
u32 gid() const override
{
return m_mount_root_inode->gid();
}
nlink_t nlinks() const override
{
return m_mount_root_inode->nlinks();
}
Result<void> chmod(mode_t mode) override
{
return m_mount_root_inode->chmod(mode);
}
Result<void> chown(u32 uid, u32 gid) override
{
return m_mount_root_inode->chown(uid, gid);
return m_mount_root_inode->set_metadata(metadata);
}
VFS::FileSystem* fs() const override
@ -82,11 +57,6 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
return m_mountee.ptr();
}
usize inode_number() const override
{
return m_mount_root_inode->inode_number();
}
VFS::InodeType type() const override
{
return VFS::InodeType::Directory;
@ -122,14 +92,14 @@ class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
return m_mount_root_inode->remove_entry(name);
}
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override
{
return m_mount_root_inode->create_file(name);
return m_mount_root_inode->create_file(name, mode);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override
{
return m_mount_root_inode->create_subdirectory(name);
return m_mount_root_inode->create_subdirectory(name, mode);
}
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
#include "fs/tmpfs/FileSystem.h"
#include "arch/Timer.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/tmpfs/Inode.h"
#include <luna/Alloc.h>
@ -10,17 +11,23 @@ namespace TmpFS
Result<SharedPtr<VFS::FileSystem>> FileSystem::create()
{
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}));
root->chmod(0755);
SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}, 0755));
fs->set_root(root);
return (SharedPtr<VFS::FileSystem>)fs;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode()
Result<u64> FileSystem::allocate_inode_number()
{
return m_next_inode_number++;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode(mode_t mode)
{
SharedPtr<FileInode> inode = TRY(make_shared<FileInode>());
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
@ -30,10 +37,11 @@ namespace TmpFS
inode->set_fs(*this, {});
TRY(inode->set_link(link, {}));
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent, mode_t mode)
{
SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());
@ -43,11 +51,13 @@ namespace TmpFS
inode->set_self(inode, {});
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
return (SharedPtr<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor, mode_t mode)
{
SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));
@ -58,6 +68,9 @@ namespace TmpFS
// FIXME: This should be queried from Device directly, but Device doesn't have an API to store and retrieve its
// device ID atm.
inode->set_device_id(luna_dev_makedev(major, minor), {});
inode->m_metadata.mode = mode;
inode->m_metadata.atime = inode->m_metadata.ctime = inode->m_metadata.mtime = *Timer::realtime_clock();
inode->m_metadata.size = device->size();
return (SharedPtr<VFS::Inode>)inode;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

72
kernel/src/net/Socket.h Normal file
View File

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

View File

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

View File

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

View File

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

18
kernel/src/sys/alarm.cpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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