diff --git a/CMakeLists.txt b/CMakeLists.txt index b1b8711f..e885d7dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,18 +175,21 @@ add_custom_target(filesystem # Set the emulator. set(EMULATOR qemu-system-i386) +# Set the type of video. +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) +# Set the amount of memory. +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m 1096M) +# Disables all default devices (e.g., serial ports, network cards, VGA +# adapters). Only devices we explicitly specify will be added. +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -nodefaults) # Set the debug type. if(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_LOG) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:${CMAKE_BINARY_DIR}/serial.log) elseif(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_STDIO) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial stdio) endif(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_LOG) -# Set the type of video. -set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) -# Set the amount of memory. -set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m 1096M) # Set the EXT2 drive. -set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -drive file=${CMAKE_BINARY_DIR}/rootfs.img,format=raw,index=0,media=disk) +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -drive file=${CMAKE_BINARY_DIR}/rootfs.img,format=raw,if=ide,index=0,media=disk) # ============================================================================= # Booting with QEMU for fun diff --git a/README.md b/README.md index 73651032..4e3e66dc 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ seconds! If you are a beginner in Operating-System developing, perhaps MentOS is the right operating system to start with. -Parts of MentOS are inherited or inspired by a similar educational operating +Parts of MentOS are inherited or inspired by a similar educational operating system called [DreamOs](https://github.com/dreamos82/DreamOs) written by Ivan Gualandri. @@ -55,47 +55,52 @@ Gualandri. Follows the list of implemented features: -**Processes and Events** - - [x] Memory protection (User vs Kernel); - - [x] Processes; - - [x] Scheduler (synchronous and asynchronous); - - [x] Interrupts and Exceptions; - - [x] Signals; - - [x] Timers and RTC; - - [x] Wait-queues; - - [x] System Calls; - - [ ] Multi-core; - -**Memory** - - [x] Paging; - - [x] Buddy System; - - [x] Slab Allocation; - - [x] Zone Allocator; - - [x] Cache Allocator; - - [x] Heap; - - [x] Virtual Addressing; - -**Filesystem** - - [x] Virtual Filesystem (VFS); - - [x] Initramfs; - - [x] Second Extended File System (EXT2); - - [x] Procfs; - -**Input/Output** - - [x] Programmable Interrupt Controller (PIC) drivers; - - [x] PS/2 drivers; - - [x] Advanced Technology Attachment (ATA) drivers; - - [x] Real Time Clock (RTC) drivers; - - [x] Keyboard drivers (IT/ENG layouts); - - [x] Video drivers; - - [ ] VGA drivers; - -**Inter-Process Communication (IPC)** - - [X] Semaphore - - [X] Message queue - - [X] Shared memory - - [ ] PIPE - - [ ] Named PIPE +### Processes and Events + +- [x] Memory protection (User vs Kernel); +- [x] Processes; +- [x] Scheduler (synchronous and asynchronous); +- [x] Interrupts and Exceptions; +- [x] Signals; +- [x] Timers and RTC; +- [x] Wait-queues; +- [x] System Calls; +- [ ] Multi-core; + +### Memory + +- [x] Paging; +- [x] Buddy System; +- [x] Slab Allocation; +- [x] Zone Allocator; +- [x] Cache Allocator; +- [x] Heap; +- [x] Virtual Addressing; + +### Filesystem + +- [x] Virtual Filesystem (VFS); +- [x] Initramfs; +- [x] Second Extended File System (EXT2); +- [x] Procfs; + +### Input/Output + +- [x] Programmable Interrupt Controller (PIC) drivers; +- [x] PS/2 drivers; +- [x] Advanced Technology Attachment (ATA) drivers; +- [x] Real Time Clock (RTC) drivers; +- [x] Keyboard drivers (IT/ENG layouts); +- [x] Video drivers; +- [ ] VGA drivers; + +### Inter-Process Communication (IPC) + +- [X] Semaphore +- [X] Message queue +- [X] Shared memory +- [ ] PIPE +- [ ] Named PIPE I will try to keep it updated... @@ -108,26 +113,26 @@ tested with *Ubuntu*, and under Windows with *WSL1* and *WSL2*. For **compiling** the system we need: - - git - - gcc - - nasm - - make - - cmake - - ccmake (suggested) - - e2fsprogs (should be already installed) +- git +- gcc +- nasm +- make +- cmake +- ccmake (suggested) +- e2fsprogs (should be already installed) Under **MacOS**, for compiling, you have additional dependencies: - - i386-elf-binutils - - i386-elf-gcc +- i386-elf-binutils +- i386-elf-gcc For **executing** the operating system we need: - - qemu-system-i386 (or qemu-system-x86) +- qemu-system-i386 (or qemu-system-x86) For **debugging** we suggest using: - - gdb or cgdb +- gdb or cgdb ### Installing the prerequisites @@ -139,6 +144,7 @@ sudo apt-get install -y git build-essential nasm make cmake cmake-curses-gui e2f sudo apt-get install -y qemu-system-x86 sudo apt-get install -y gdb cgdb ``` + Note: Older versions might have `qemu-system-i386` instead of `qemu-system-x86`. *[Back to the Table of Contents](#table-of-contents)* @@ -164,6 +170,7 @@ Generate the EXT2 filesystem with: ```bash make filesystem ``` + you just need to generate the filesystem once. If you change a `program` you need to re-generate the entire filesystem with `make filesystem`, but this will override any changes you made to the files inside the `rootfs.img`. In the future I will find a way to update just the `/usr/bin` directory and the programs. *[Back to the Table of Contents](#table-of-contents)* @@ -184,12 +191,13 @@ To login, use one of the usernames listed in `files/etc/passwd`. For booting MentOS from GRUB in QEMU we need the following tools: - - xorriso - - grub-mkrescue (from grub-common) +- xorriso +- grub-mkrescue (from grub-common) We also need `grub-pc-bin`, otherwise GRUB won't start in QEMU. Which can be installed in Ubuntu with the following command: + ```bash sudo apt-get update && sudo apt-get upgrade -y sudo apt-get install -y grub-common grub-pc-bin xorriso @@ -210,6 +218,7 @@ This section explains how to add a new program to MentOS, and also how to run pr ### Create a new program Head to the `programs` (or the `programs/tests`) folder. Create and open a new program, for instance a file called `hello_world.c`, with your preferred editor, and add this content to the file: + ```C #include @@ -221,10 +230,12 @@ int main(int argc, char *argv[]) ``` ### Add the new program to the list of compiled sources + Now we can add the program to the list of files which are compiled and placed inside MentOS filesystem. The following procedure is the same for both `programs` and `programs/tests`, what changes is which `CMakeLists.txt` file we modify. You need to modify the `CMakeLists.txt` file, either `programs/CMakeLists.txt` or `programs/tests/CMakeLists.txt`, and add your program to the list of files to be compiled: + ```Makefile # Add the executables (manually). set(PROGRAMS @@ -233,7 +244,9 @@ set(PROGRAMS hello_world.c ) ``` + or + ```Makefile # Add the executables (manually). set(TESTS @@ -246,14 +259,18 @@ set(TESTS That's it, the `hello_world.c` file will be compiled and will appear inside the `/bin` or `/bin/tests` folder of MentOS. ### Running a program or a test + Once you login into MentOS, if you placed your source code in `programs`, you can execute the program by simply typing: + ```bash hello_world ``` + because the file resides in `/bin`, and that folder is listed in the `PATH` environment variable. Now, if you placed your source code inside the `programs/tests` folder, the executable will end up inside the `/bin/tests` folder. However, the `/bin/tests` folder is not listed in `PATH`, so, if you want to execute a test from that folder you need to specify the full path: + ```bash /bin/tests/hello_world ``` @@ -261,9 +278,11 @@ However, the `/bin/tests` folder is not listed in `PATH`, so, if you want to exe *[Back to the Table of Contents](#table-of-contents)* ## Kernel logging + The kernel provides ways of printing logging messages *from* inside the kernel code *to* the bash where you executed the `make qemu`. These *logging* functions are: + ```C #define pr_emerg(...) #define pr_alert(...) @@ -277,6 +296,7 @@ These *logging* functions are: ``` You use them like you would use a `printf`: + ```C if (fd < 0) { pr_err("Failed to open file '%s', received file descriptor %d.\n", filename, fd); @@ -287,6 +307,7 @@ You use them like you would use a `printf`: By default only message that goes from `pr_notice` included down to `pr_emerg` are displayed. Each logging function (they are actually macros) is a wrapper that automatically sets the desired **log level**. Each log level is identified by a number, and declared as follows: + ```C #define LOGLEVEL_DEFAULT (-1) ///< default-level messages. #define LOGLEVEL_EMERG 0 ///< system is unusable. @@ -300,6 +321,7 @@ Each logging function (they are actually macros) is a wrapper that automatically ``` You can change the logging level by including the following lines at the beginning of your source code: + ```C // Include the kernel log levels. #include "sys/kernel_levels.h" @@ -310,6 +332,7 @@ You can change the logging level by including the following lines at the beginni // Include the debuggin header. #include "io/debug.h" ``` + This example sets the `__DEBUG_LEVEL__`, so that all the messages from `INFO` and below are shown. While `__DEBUG_HEADER__` is just a string that is automatically prepended to your message, helping you identifying from which code the message is coming from. *[Back to the Table of Contents](#table-of-contents)* @@ -318,15 +341,15 @@ This example sets the `__DEBUG_LEVEL__`, so that all the messages from `INFO` an MentOS supports scheduling algorithms for aperiodic: - - Round-Robin (RR) - - Highest Priority - - Completely Fair Scheduling (CFS) - - Aperiodic Earliest Deadline First (AEDF) +- Round-Robin (RR) +- Highest Priority +- Completely Fair Scheduling (CFS) +- Aperiodic Earliest Deadline First (AEDF) It also supports periodic algorithms: - - Earliest Deadline First (EDF) - - Rate Monotonic (RM) +- Earliest Deadline First (EDF) +- Rate Monotonic (RM) If you want to change the scheduling algorithm, to Round-Robin (RR) for instance: @@ -362,7 +385,7 @@ ccmake .. Now you should see something like this: -``` +```cmake BUILD_DOCUMENTATION ON CMAKE_BUILD_TYPE CMAKE_INSTALL_PREFIX /usr/local @@ -393,37 +416,45 @@ make ``` Then, you need to generate a file called `.gdbinit` placed inside the `build` directory, which will tell **gdb** which *object* file he needs to read in order to allow proper debugging. Basically, it provides for each binary file, the location of their `.text` section. To generate the file, just execute: + ```bash make gdbinit ``` Finally, you run qemu in debugging mode with: + ```bash make qemu-gdb ``` If you did everything correctly, you should see an empty QEMU window. Basically, QEMU is waiting for you to connect *remotely* with gdb. Anyway, running `make qemu-gdb` will make your current shell busy, you cannot call `gdb` in it. You need to open a new shell inside the `build` folder and do a: + ```bash gdb --quiet --command=gdb.run ``` + or + ```bash cgdb --quiet --command=gdb.run ``` Now you should have in front of you: - 1. the QEMU window waiting for you; - 2. the **first** shell where you ran `make qemu-gdb`, which is also waiting for you; - 3. the **second** shell where `gdb` is runnign and, you guessed it, is waiting for you. + +1. the QEMU window waiting for you; +2. the **first** shell where you ran `make qemu-gdb`, which is also waiting for you; +3. the **second** shell where `gdb` is runnign and, you guessed it, is waiting for you. By default I placed a breakpoint at the begginning of (1) the *bootloader* and (2) the *kernel* itself. So, when gdb starts you need to first give a continue: + ```bash (gdb) continue ``` This will make the kernel run, and stop at the **first** breakpoint which is inside the *bootloader*: + ```bash Breakpoint 1, boot_main (...) at .../mentos/src/boot.c:220 220 { @@ -432,6 +463,7 @@ Breakpoint 1, boot_main (...) at .../mentos/src/boot.c:220 giving a second `continue` will get you to the start of the operating system: This will make the kernel run, and stop at the **second** breakpoint which is inside the *kernel*: + ```bash Breakpoint 2, kmain (...) at .../mentos/src/kernel.c:95 95 { @@ -446,27 +478,28 @@ to connect to the running process. Project Manager: -* [Enrico Fraccaroli](https://github.com/Galfurian) +- [Enrico Fraccaroli](https://github.com/Galfurian) Developers: -* [Alessandro Danese](https://github.com/alessandroDanese88), [Luigi Capogrosso](https://github.com/luigicapogrosso), [Mirco De Marchi](https://github.com/mircodemarchi) - - Protection ring - - libc -* Andrea Cracco - - Buddy System, Heap, Paging, Slab, Caching, Zone - - Process Image, ELF - - VFS: procfs - - Bootloader -* Linda Sacchetto, Marco Berti - - Real time scheduler -* Daniele Nicoletti, Filippo Ziche - - Real time scheduler (Asynchronous EDF) - - Soft IRQs - - Timer - - Signals -* And many other valuable contributors - * [rouseabout](https://github.com/rouseabout) - * [seekbytes](https://github.com/seekbytes) - * [fischerling](https://github.com/fischerling) + +- [Alessandro Danese](https://github.com/alessandroDanese88), [Luigi Capogrosso](https://github.com/luigicapogrosso), [Mirco De Marchi](https://github.com/mircodemarchi) + - Protection ring + - libc +- Andrea Cracco + - Buddy System, Heap, Paging, Slab, Caching, Zone + - Process Image, ELF + - VFS: procfs + - Bootloader +- Linda Sacchetto, Marco Berti + - Real time scheduler +- Daniele Nicoletti, Filippo Ziche + - Real time scheduler (Asynchronous EDF) + - Soft IRQs + - Timer + - Signals +- And many other valuable contributors + - [rouseabout](https://github.com/rouseabout) + - [seekbytes](https://github.com/seekbytes) + - [fischerling](https://github.com/fischerling) *[Back to the Table of Contents](#table-of-contents)* diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 91f0a437..a50bc820 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -74,24 +74,19 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/inc/klib/irqflags.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/spinlock.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/stdatomic.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/ndtree.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/stack_helper.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/mutex.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/compiler.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/rbtree.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/hashmap.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/list.h ${CMAKE_SOURCE_DIR}/mentos/inc/boot.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/types.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/reboot.h ${CMAKE_SOURCE_DIR}/mentos/inc/sys/module.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/utsname.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_font.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_palette.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_mode.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/proc_modules.h ${CMAKE_SOURCE_DIR}/mentos/inc/io/video.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/debug.h ${CMAKE_SOURCE_DIR}/mentos/inc/devices/pci.h ${CMAKE_SOURCE_DIR}/mentos/inc/devices/fpu.h ${CMAKE_SOURCE_DIR}/mentos/inc/system/printk.h @@ -100,65 +95,12 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/inc/system/signal.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ext2.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/pipe.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/namei.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/attr.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/procfs.h ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs_types.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ioctl.h - ${CMAKE_SOURCE_DIR}/libc/inc/limits.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdbool.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdint.h - ${CMAKE_SOURCE_DIR}/libc/inc/time.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/termios-struct.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/ioctls.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/stat.h - ${CMAKE_SOURCE_DIR}/libc/inc/shadow.h - ${CMAKE_SOURCE_DIR}/libc/inc/fcvt.h - ${CMAKE_SOURCE_DIR}/libc/inc/string.h - ${CMAKE_SOURCE_DIR}/libc/inc/pwd.h - ${CMAKE_SOURCE_DIR}/libc/inc/readline.h - ${CMAKE_SOURCE_DIR}/libc/inc/crypt/sha256.h - ${CMAKE_SOURCE_DIR}/libc/inc/termios.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdlib.h - ${CMAKE_SOURCE_DIR}/libc/inc/fcntl.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdio.h - ${CMAKE_SOURCE_DIR}/libc/inc/ring_buffer.h - ${CMAKE_SOURCE_DIR}/libc/inc/err.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdarg.h - ${CMAKE_SOURCE_DIR}/libc/inc/libgen.h - ${CMAKE_SOURCE_DIR}/libc/inc/sched.h - ${CMAKE_SOURCE_DIR}/libc/inc/assert.h - ${CMAKE_SOURCE_DIR}/libc/inc/stddef.h - ${CMAKE_SOURCE_DIR}/libc/inc/signal.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/errno.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/shm.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/bitops.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/types.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/sem.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/reboot.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/msg.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/ipc.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/wait.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/list_head.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/kernel_levels.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/ioctl.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/mman.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/utsname.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/stat.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/list_head_algorithm.h - ${CMAKE_SOURCE_DIR}/libc/inc/grp.h - ${CMAKE_SOURCE_DIR}/libc/inc/unistd.h - ${CMAKE_SOURCE_DIR}/libc/inc/dirent.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/ansi_colors.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/mm_io.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/debug.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/port_io.h - ${CMAKE_SOURCE_DIR}/libc/inc/ctype.h - ${CMAKE_SOURCE_DIR}/libc/inc/system/syscall_types.h - ${CMAKE_SOURCE_DIR}/libc/inc/array.h - ${CMAKE_SOURCE_DIR}/libc/inc/strerror.h - ${CMAKE_SOURCE_DIR}/libc/inc/math.h - + ${CMAKE_SOURCE_DIR}/mentos/kernel.lds ${CMAKE_SOURCE_DIR}/mentos/src/drivers/mouse.c ${CMAKE_SOURCE_DIR}/mentos/src/drivers/rtc.c ${CMAKE_SOURCE_DIR}/mentos/src/drivers/mem.c @@ -170,6 +112,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/src/elf/elf.c ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler_feedback.c ${CMAKE_SOURCE_DIR}/mentos/src/process/process.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/user.S ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler_algorithm.c ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler.c ${CMAKE_SOURCE_DIR}/mentos/src/process/wait.c @@ -177,17 +120,23 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/src/mem/zone_allocator.c ${CMAKE_SOURCE_DIR}/mentos/src/mem/paging.c ${CMAKE_SOURCE_DIR}/mentos/src/mem/buddysystem.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/libbuddysystem.a ${CMAKE_SOURCE_DIR}/mentos/src/mem/kheap.c ${CMAKE_SOURCE_DIR}/mentos/src/mem/slab.c ${CMAKE_SOURCE_DIR}/mentos/src/mem/vmem_map.c ${CMAKE_SOURCE_DIR}/mentos/src/hardware/timer.c ${CMAKE_SOURCE_DIR}/mentos/src/hardware/pic8259.c ${CMAKE_SOURCE_DIR}/mentos/src/hardware/cpuid.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.S ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.S ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.S ${CMAKE_SOURCE_DIR}/mentos/src/ipc/sem.c ${CMAKE_SOURCE_DIR}/mentos/src/ipc/msg.c ${CMAKE_SOURCE_DIR}/mentos/src/ipc/ipc.c @@ -209,6 +158,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/src/klib/ctype.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/spinlock.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/math.c + ${CMAKE_SOURCE_DIR}/mentos/src/boot.S ${CMAKE_SOURCE_DIR}/mentos/src/sys/utsname.c ${CMAKE_SOURCE_DIR}/mentos/src/sys/module.c ${CMAKE_SOURCE_DIR}/mentos/src/io/debug.c @@ -234,14 +184,73 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/src/fs/readdir.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/ext2.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/read_write.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/fcntl.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/stat.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/ioctl.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/vfs.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/attr.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/pipe.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/procfs.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/namei.c ${CMAKE_SOURCE_DIR}/mentos/src/multiboot.c ${CMAKE_SOURCE_DIR}/mentos/src/kernel/sys.c + + ${CMAKE_SOURCE_DIR}/libc/inc/errno.h + ${CMAKE_SOURCE_DIR}/libc/inc/limits.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdbool.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdint.h + ${CMAKE_SOURCE_DIR}/libc/inc/time.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/termios-struct.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/ioctls.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/stat.h + ${CMAKE_SOURCE_DIR}/libc/inc/shadow.h + ${CMAKE_SOURCE_DIR}/libc/inc/fcvt.h + ${CMAKE_SOURCE_DIR}/libc/inc/string.h + ${CMAKE_SOURCE_DIR}/libc/inc/pwd.h + ${CMAKE_SOURCE_DIR}/libc/inc/readline.h + ${CMAKE_SOURCE_DIR}/libc/inc/crypt/sha256.h + ${CMAKE_SOURCE_DIR}/libc/inc/termios.h + ${CMAKE_SOURCE_DIR}/libc/inc/syslog.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdlib.h + ${CMAKE_SOURCE_DIR}/libc/inc/fcntl.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdio.h + ${CMAKE_SOURCE_DIR}/libc/inc/ring_buffer.h + ${CMAKE_SOURCE_DIR}/libc/inc/err.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdarg.h + ${CMAKE_SOURCE_DIR}/libc/inc/libgen.h + ${CMAKE_SOURCE_DIR}/libc/inc/sched.h + ${CMAKE_SOURCE_DIR}/libc/inc/assert.h + ${CMAKE_SOURCE_DIR}/libc/inc/stddef.h + ${CMAKE_SOURCE_DIR}/libc/inc/signal.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/shm.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/bitops.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/types.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/sem.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/reboot.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/msg.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/ipc.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/wait.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/kernel_levels.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/ioctl.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/mman.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/utsname.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/stat.h + ${CMAKE_SOURCE_DIR}/libc/inc/list_head.h + ${CMAKE_SOURCE_DIR}/libc/inc/grp.h + ${CMAKE_SOURCE_DIR}/libc/inc/ndtree.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/ansi_colors.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/mm_io.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/port_io.h + ${CMAKE_SOURCE_DIR}/libc/inc/ctype.h + ${CMAKE_SOURCE_DIR}/libc/inc/dirent.h + ${CMAKE_SOURCE_DIR}/libc/inc/hashmap.h + ${CMAKE_SOURCE_DIR}/libc/inc/system/syscall_types.h + ${CMAKE_SOURCE_DIR}/libc/inc/list.h + ${CMAKE_SOURCE_DIR}/libc/inc/array.h + ${CMAKE_SOURCE_DIR}/libc/inc/strerror.h + ${CMAKE_SOURCE_DIR}/libc/inc/math.h + ${CMAKE_SOURCE_DIR}/libc/inc/unistd.h + ${CMAKE_SOURCE_DIR}/libc/inc/list_head_algorithm.h ${CMAKE_SOURCE_DIR}/libc/src/stdlib.c ${CMAKE_SOURCE_DIR}/libc/src/libc_start.c ${CMAKE_SOURCE_DIR}/libc/src/setenv.c @@ -257,6 +266,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/src/unistd/symlink.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/getsid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/read.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/fcntl.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/readlink.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/chmod.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/mkdir.c @@ -274,6 +284,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/src/unistd/close.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/waitpid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/chown.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/pipe.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/creat.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/nice.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/lseek.c @@ -293,10 +304,15 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/src/pwd.c ${CMAKE_SOURCE_DIR}/libc/src/libgen.c ${CMAKE_SOURCE_DIR}/libc/src/string.c + ${CMAKE_SOURCE_DIR}/libc/src/crt0.S + ${CMAKE_SOURCE_DIR}/libc/src/hashmap.c + ${CMAKE_SOURCE_DIR}/libc/src/ndtree.c ${CMAKE_SOURCE_DIR}/libc/src/assert.c ${CMAKE_SOURCE_DIR}/libc/src/grp.c ${CMAKE_SOURCE_DIR}/libc/src/shadow.c ${CMAKE_SOURCE_DIR}/libc/src/sched.c + ${CMAKE_SOURCE_DIR}/libc/src/list.c + ${CMAKE_SOURCE_DIR}/libc/src/syslog.c ${CMAKE_SOURCE_DIR}/libc/src/strerror.c ${CMAKE_SOURCE_DIR}/libc/src/sys/mman.c ${CMAKE_SOURCE_DIR}/libc/src/sys/errno.c @@ -307,17 +323,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/src/ctype.c ${CMAKE_SOURCE_DIR}/libc/src/readline.c ${CMAKE_SOURCE_DIR}/libc/src/termios.c - ${CMAKE_SOURCE_DIR}/libc/src/io/debug.c ${CMAKE_SOURCE_DIR}/libc/src/io/mm_io.c ${CMAKE_SOURCE_DIR}/libc/src/math.c - - ${CMAKE_SOURCE_DIR}/mentos/src/process/user.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.S - ${CMAKE_SOURCE_DIR}/mentos/src/boot.S - ${CMAKE_SOURCE_DIR}/libc/src/crt0.S ) endif (DOXYGEN_FOUND) \ No newline at end of file diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index ffd0278d..3531ece0 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -27,9 +27,12 @@ add_library( ${CMAKE_SOURCE_DIR}/libc/src/setenv.c ${CMAKE_SOURCE_DIR}/libc/src/assert.c ${CMAKE_SOURCE_DIR}/libc/src/abort.c + ${CMAKE_SOURCE_DIR}/libc/src/syslog.c + ${CMAKE_SOURCE_DIR}/libc/src/ndtree.c + ${CMAKE_SOURCE_DIR}/libc/src/list.c + ${CMAKE_SOURCE_DIR}/libc/src/hashmap.c ${CMAKE_SOURCE_DIR}/libc/src/crypt/sha256.c ${CMAKE_SOURCE_DIR}/libc/src/io/mm_io.c - ${CMAKE_SOURCE_DIR}/libc/src/io/debug.c ${CMAKE_SOURCE_DIR}/libc/src/sys/ipc.c ${CMAKE_SOURCE_DIR}/libc/src/sys/unistd.c ${CMAKE_SOURCE_DIR}/libc/src/sys/errno.c @@ -42,6 +45,7 @@ add_library( ${CMAKE_SOURCE_DIR}/libc/src/unistd/getppid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/getpid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/exit.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/fcntl.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/setsid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/getsid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/setpgid.c @@ -56,6 +60,7 @@ add_library( ${CMAKE_SOURCE_DIR}/libc/src/unistd/exec.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/nice.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/open.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/pipe.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/reboot.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/waitpid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/chdir.c @@ -86,3 +91,8 @@ set_target_properties(libc PROPERTIES PREFIX "") if(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_LOG) target_compile_definitions(libc PUBLIC EMULATOR_OUTPUT_LOG) endif() + +target_compile_definitions( + libc PUBLIC + MENTOS_ROOT="${CMAKE_SOURCE_DIR}" +) diff --git a/libc/inc/bits/stat.h b/libc/inc/bits/stat.h index e8513c5b..83a09463 100644 --- a/libc/inc/bits/stat.h +++ b/libc/inc/bits/stat.h @@ -9,6 +9,7 @@ #error "Never include directly; use instead." #endif +#include "sys/types.h" #include "stddef.h" #include "time.h" @@ -18,14 +19,22 @@ typedef struct stat { dev_t st_dev; /// File serial number. ino_t st_ino; - /// Mode of file. + /// Mode of file (file type and permissions). mode_t st_mode; - /// File user id. + /// Number of hard links to the file. + nlink_t st_nlink; + /// File user ID. uid_t st_uid; - /// File group id. + /// File group ID. gid_t st_gid; - /// File Size. + /// Device ID (if special file). + dev_t st_rdev; + /// File size in bytes. off_t st_size; + /// Preferred block size for filesystem I/O. + blksize_t st_blksize; + /// Number of blocks allocated for the file. + blkcnt_t st_blocks; /// Time of last access. time_t st_atime; /// Time of last data modification. diff --git a/libc/inc/sys/errno.h b/libc/inc/errno.h similarity index 100% rename from libc/inc/sys/errno.h rename to libc/inc/errno.h diff --git a/libc/inc/fcntl.h b/libc/inc/fcntl.h index 991f527b..ab000350 100644 --- a/libc/inc/fcntl.h +++ b/libc/inc/fcntl.h @@ -1,17 +1,57 @@ /// @file fcntl.h -/// @brief Headers of functions fcntl() and open(). +/// @brief Header file for file control options and commands. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#define O_ACCMODE 0003 ///< Bits defining the open mode -#define O_RDONLY 00000000U ///< Open for reading only. -#define O_WRONLY 00000001U ///< Open for writing only. -#define O_RDWR 00000002U ///< Open for reading and writing. -#define O_CREAT 00000100U ///< Create if nonexistant. -#define O_EXCL 00000200U ///< Error if already exists. -#define O_TRUNC 00001000U ///< Truncate to zero length. +/// @name File Access Modes +/// @brief Defines file access modes for opening files. +/// @{ +#define O_ACCMODE 0003 ///< Bits defining the open mode. +#define O_RDONLY 00000000U ///< Open for reading only. +#define O_WRONLY 00000001U ///< Open for writing only. +#define O_RDWR 00000002U ///< Open for reading and writing. +/// @} + +/// @name File Creation Flags +/// @brief Flags for file creation, controlling how files are opened or created. +/// @{ +#define O_CREAT 00000100U ///< Create file if it does not exist. +#define O_EXCL 00000200U ///< Error if file already exists. +#define O_NOCTTY 00000400U ///< Do not assign controlling terminal. +#define O_TRUNC 00001000U ///< Truncate file to zero length. #define O_APPEND 00002000U ///< Set append mode. -#define O_NONBLOCK 00004000U ///< No delay. -#define O_DIRECTORY 00200000U ///< If file exists has no effect. Otherwise, the file is created. +#define O_NONBLOCK 00004000U ///< Enable non-blocking mode. +#define O_DIRECTORY 00200000U ///< Open only if it is a directory. +/// @} + +/// @name fcntl Commands +/// @brief Commands for the fcntl() function, used for managing file descriptors. +/// @{ +#define F_DUPFD 0 ///< Duplicate file descriptor. +#define F_GETFD 1 ///< Get file descriptor flags. +#define F_SETFD 2 ///< Set file descriptor flags. +#define F_GETFL 3 ///< Get file status flags. +#define F_SETFL 4 ///< Set file status flags. +#define F_GETOWN 5 ///< Get process receiving SIGIO signals. +#define F_SETOWN 6 ///< Set process receiving SIGIO signals. +#define F_GETLK 7 ///< Get record locking information. +#define F_SETLK 8 ///< Set record locking information. +#define F_SETLKW 9 ///< Set record locking info; wait if blocked. +/// @} + +/// @name Lock Operation Flags +/// @brief Flags for the fcntl function lock operations. +/// @{ +#define F_RDLCK 1 ///< Shared or read lock. +#define F_WRLCK 2 ///< Exclusive or write lock. +#define F_UNLCK 3 ///< Unlock. +/// @} + +/// @brief Provides control operations on an open file descriptor. +/// @param fd The file descriptor on which to perform the operation. +/// @param request The `fcntl` command, defining the operation (e.g., `F_GETFL`, `F_SETFL`). +/// @param data Additional data required by certain `fcntl` commands (e.g., flags or pointer). +/// @return Returns 0 on success; on error, returns a negative error code. +long fcntl(int fd, unsigned int request, unsigned long data); diff --git a/libc/inc/hashmap.h b/libc/inc/hashmap.h new file mode 100644 index 00000000..ccf2bc4b --- /dev/null +++ b/libc/inc/hashmap.h @@ -0,0 +1,63 @@ +/// @file hashmap.h +/// @brief Header file for a hashmap implementation with `char *` keys. + +#pragma once + +#include + +/// Define the size of the hashmap. +#define HASHMAP_SIZE 1024 + +/// @brief Structure representing a hashmap entry. +typedef struct hashmap_entry { + /// The precomputed hash of the key. + size_t hash; + /// The value associated with the key. + void *value; + /// Pointer to the next entry (for handling collisions). + struct hashmap_entry *next; +} hashmap_entry_t; + +/// @brief Structure representing the hashmap. +typedef struct { + /// Array of linked lists for separate chaining. + hashmap_entry_t *buckets[HASHMAP_SIZE]; + /// Function to allocate an entry. + hashmap_entry_t *(*alloc_entry)(void); + /// Function to deallocate an entry. + void (*dealloc_entry)(hashmap_entry_t *); +} hashmap_t; + +/// @brief Hash function for generating an index from a key. +/// @param key The key to hash. +/// @return The hash index for the key. +size_t hash(const char *key); + +/// @brief Initializes the hashmap with custom alloc and dealloc functions for +/// entries. +/// @param map Pointer to the hashmap to initialize. +/// @param alloc_fn Function to allocate entries, or NULL to use the default. +/// @param dealloc_fn Function to deallocate entries, or NULL to use the +/// default. +void hashmap_init(hashmap_t *map, hashmap_entry_t *(*alloc_fn)(void), void (*dealloc_fn)(hashmap_entry_t *)); + +/// @brief Inserts a key-value pair into the hashmap. +/// @param map Pointer to the hashmap. +/// @param key The key for the value. +/// @param value The value to store associated with the key. +void hashmap_insert(hashmap_t *map, const char *key, void *value); + +/// @brief Retrieves the value associated with a given key. +/// @param map Pointer to the hashmap. +/// @param key The key to search for. +/// @return The value associated with the key, or NULL if the key is not found. +void *hashmap_get(hashmap_t *map, const char *key); + +/// @brief Removes a key-value pair from the hashmap. +/// @param map Pointer to the hashmap. +/// @param key The key to remove. +void hashmap_remove(hashmap_t *map, const char *key); + +/// @brief Destroys the hashmap and frees all allocated memory. +/// @param map Pointer to the hashmap to destroy. +void hashmap_destroy(hashmap_t *map); diff --git a/libc/inc/io/debug.h b/libc/inc/io/debug.h deleted file mode 100644 index 2779343d..00000000 --- a/libc/inc/io/debug.h +++ /dev/null @@ -1,127 +0,0 @@ -/// @file debug.h -/// @brief Debugging primitives. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "sys/kernel_levels.h" - -#ifndef __DEBUG_LEVEL__ -/// Defines the debug level, by default we set it to notice. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE -#endif - -#ifndef __DEBUG_HEADER__ -/// Header for identifying outputs coming from a mechanism. -#define __DEBUG_HEADER__ 0 -#endif - -/// @brief Extract the filename from the full path provided by __FILE__. -#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) - -/// @brief Sets the loglevel. -/// @param level The new loglevel. -void set_log_level(int level); - -/// @brief Returns the current loglevel. -/// @return The current loglevel -int get_log_level(void); - -/// @brief Prints the given character to debug output. -/// @param c The character to print. -void dbg_putchar(char c); - -/// @brief Prints the given string to debug output. -/// @param s The string to print. -void dbg_puts(const char *s); - -/// @brief Prints the given string to the debug output. -/// @param file the name of the file. -/// @param fun the name of the function. -/// @param line the line inside the file. -/// @param header the header to print. -/// @param log_level the log level. -/// @param format the format to used, see printf. -/// @param ... the list of arguments. -void dbg_printf(const char *file, const char *fun, int line, char *header, short log_level, const char *format, ...); - -/// @brief Transforms the given amount of bytes to a readable string. -/// @param bytes The bytes to turn to string. -/// @return String representing the bytes in human readable form. -const char *to_human_size(unsigned long bytes); - -/// @brief Transforms the given value to a binary string. -/// @param value to print. -/// @param length of the binary output. -/// @return String representing the binary value. -const char *dec_to_binary(unsigned long value, unsigned length); - -/// Prints a default message, which is always shown. -#define pr_default(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_DEFAULT, __VA_ARGS__) - -/// Prints an emergency message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_EMERG -#define pr_emerg(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_EMERG, __VA_ARGS__) -#else -#define pr_emerg(...) -#endif - -/// Prints an alert message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_ALERT -#define pr_alert(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_ALERT, __VA_ARGS__) -#else -#define pr_alert(...) -#endif - -/// Prints a critical message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_CRIT -#define pr_crit(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_CRIT, __VA_ARGS__) -#else -#define pr_crit(...) -#endif - -/// Prints an error message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_ERR -#define pr_err(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_ERR, __VA_ARGS__) -#else -#define pr_err(...) -#endif - -/// Prints a warning message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_WARNING -#define pr_warning(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_WARNING, __VA_ARGS__) -#else -#define pr_warning(...) -#endif - -/// Prints a notice message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_NOTICE -#define pr_notice(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_NOTICE, __VA_ARGS__) -#else -#define pr_notice(...) -#endif - -/// Prints a info message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_INFO -#define pr_info(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_INFO, __VA_ARGS__) -#else -#define pr_info(...) -#endif - -/// Prints a debug message. -#if __DEBUG_LEVEL__ >= LOGLEVEL_DEBUG -#define pr_debug(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_DEBUG, __VA_ARGS__) -#else -#define pr_debug(...) -#endif - -#ifdef __KERNEL__ - -struct pt_regs; - -/// @brief Prints the registers on debug output. -/// @param frame Pointer to the register. -void dbg_print_regs(struct pt_regs *frame); - -#endif diff --git a/libc/inc/list.h b/libc/inc/list.h new file mode 100644 index 00000000..91860a74 --- /dev/null +++ b/libc/inc/list.h @@ -0,0 +1,103 @@ +/// @file list.h +/// @brief An implementation for a generic list. +/// @details Provides a generic doubly linked list with allocation support. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "list_head.h" + +#include +#include +#include + +/// @brief Represents the node of a list. +typedef struct listnode_t { + list_head list; ///< List structure for this node. + void *value; ///< Pointer to node's value. +} listnode_t; + +/// @brief Represents the list. +typedef struct list_t { + list_head head; ///< Head of the list. + unsigned int size; ///< Size of the list. + listnode_t *(*alloc)(void); ///< Node allocation function. + void (*dealloc)(listnode_t *); ///< Node deallocation function. +} list_t; + +/// @brief Initializes the list with custom alloc and dealloc functions. +/// @param list The list to initialize. +/// @param alloc_fn Function to allocate nodes. +/// @param dealloc_fn Function to deallocate nodes. +void list_init(list_t *list, listnode_t *(*alloc_fn)(void), + void (*dealloc_fn)(listnode_t *)); + +/// @brief Returns the size of the list. +/// @param list The list to get the size of. +/// @return The number of elements in the list. +static inline unsigned int list_size(const list_t *list) { + assert(list && "List is null."); + return list->size; +} + +/// @brief Checks if the list is empty. +/// @param list The list to check. +/// @return 1 if the list is empty, 0 otherwise. +static inline int list_empty(const list_t *list) { + assert(list && "List is null."); + return list_head_empty(&list->head); +} + +/// @brief Insert a new element at the front of the list. +/// @param list The list to insert into. +/// @param value The value to store in the new node. +/// @return Pointer to the new node. +listnode_t *list_insert_front(list_t *list, void *value); + +/// @brief Insert a new element at the back of the list. +/// @param list The list to insert into. +/// @param value The value to store in the new node. +/// @return Pointer to the new node. +listnode_t *list_insert_back(list_t *list, void *value); + +/// @brief Removes a specific node from the list. +/// @param list The list to remove the node from. +/// @param node The node to remove. +/// @return The value stored in the removed node. +void *list_remove_node(list_t *list, listnode_t *node); + +/// @brief Removes the front element of the list. +/// @param list The list to remove from. +/// @return The value of the removed front node. +void *list_remove_front(list_t *list); + +/// @brief Removes the back element of the list. +/// @param list The list to remove from. +/// @return The value of the removed back node. +void *list_remove_back(list_t *list); + +/// @brief Destroys the list and deallocates all nodes. +/// @param list The list to destroy. +void list_destroy(list_t *list); + +/// @brief Finds the node with the specified value. +/// @param list The list to search. +/// @param value The value to search for. +/// @return Pointer to the node if found, NULL otherwise. +listnode_t *list_find(list_t *list, void *value); + +/// @brief Returns the value at the front of the list without removing it. +/// @param list The list to peek at. +/// @return The value at the front of the list, or NULL if the list is empty. +void *list_peek_front(const list_t *list); + +/// @brief Returns the value at the back of the list without removing it. +/// @param list The list to peek at. +/// @return The value at the back of the list, or NULL if the list is empty. +void *list_peek_back(const list_t *list); + +/// @brief Merges two lists destructively, appending the source list to the target. +/// @param target The target list to append to. +/// @param source The source list to merge from, which will be re-initialized as empty. +void list_merge(list_t *target, list_t *source); diff --git a/libc/inc/sys/list_head.h b/libc/inc/list_head.h similarity index 100% rename from libc/inc/sys/list_head.h rename to libc/inc/list_head.h diff --git a/libc/inc/sys/list_head_algorithm.h b/libc/inc/list_head_algorithm.h similarity index 100% rename from libc/inc/sys/list_head_algorithm.h rename to libc/inc/list_head_algorithm.h diff --git a/libc/inc/ndtree.h b/libc/inc/ndtree.h new file mode 100644 index 00000000..20c7509f --- /dev/null +++ b/libc/inc/ndtree.h @@ -0,0 +1,105 @@ +/// @file ndtree.h +/// @brief N-Dimensional tree implementation. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "list_head.h" + +/// @brief Stores data about an NDTree node. +typedef struct ndtree_node { + void *value; ///< User-provided value, used indirectly via ndtree_tree_compare_f. + struct ndtree_node *parent; ///< Pointer to the parent. + struct list_head siblings; ///< List of siblings. + struct list_head children; ///< List of children. +} ndtree_node_t; + +/// @brief Function pointer type for comparing elements in the tree. +/// @param lhs Left-hand side value to compare. +/// @param rhs Right-hand side value to compare. +/// @return Comparison result: <0 if lhs < rhs, 0 if lhs == rhs, >0 if lhs > rhs. +typedef int (*ndtree_tree_compare_f)(void *lhs, void *rhs); + +/// @brief Callback function type for operating on tree nodes. +/// @param node The node to operate on. +typedef void (*ndtree_tree_node_f)(ndtree_node_t *node); + +/// @brief Custom allocator for tree nodes. +/// @param value The value to store in the node. +/// @return Pointer to the allocated node. +typedef ndtree_node_t *(*ndtree_alloc_node_f)(void *value); + +/// @brief Custom deallocator for tree nodes. +/// @param node The node to deallocate. +typedef void (*ndtree_free_node_f)(ndtree_node_t *node); + +/// @brief Stores data about an NDTree. +typedef struct ndtree { + unsigned size; ///< Size of the tree. + ndtree_node_t *root; ///< Pointer to the root node. + struct list_head orphans; ///< List of orphans. + ndtree_tree_compare_f compare_node; ///< Custom node comparison. + ndtree_alloc_node_f alloc_node; ///< Custom allocator for nodes. + ndtree_free_node_f free_node; ///< Custom deallocator for nodes. +} ndtree_t; + +/// @brief Initializes a tree with comparison, allocation, and deallocation functions. +/// @param tree The tree to initialize (already allocated by the user). +/// @param compare_node Comparison function for nodes. +/// @param alloc_node Custom allocator for nodes. +/// @param free_node Custom deallocator for nodes. +void ndtree_tree_init(ndtree_t *tree, ndtree_tree_compare_f compare_node, ndtree_alloc_node_f alloc_node, ndtree_free_node_f free_node); + +/// @brief Initializes a tree node with a given value. +/// @param node The node to initialize. +/// @param value The value to store in the node. +void ndtree_node_init(ndtree_node_t *node, void *value); + +/// @brief Creates a root node for the tree. +/// @param tree The tree to add the root node to. +/// @param value The value for the root node. +/// @return Pointer to the newly created root node, or NULL on failure. +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value); + +/// @brief Adds a child node to a specified parent node. +/// @param tree The tree containing the nodes. +/// @param parent The parent node. +/// @param child The child node to add. +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child); + +/// @brief Creates and adds a child node to a specified parent node. +/// @param tree The tree to add the child to. +/// @param parent The parent node. +/// @param value The value for the new child node. +/// @return Pointer to the newly created child node, or NULL on failure. +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value); + +/// @brief Counts the number of children of a node. +/// @param node The node to count children for. +/// @return The number of children of the node. +unsigned int ndtree_node_count_children(ndtree_node_t *node); + +/// @brief Deallocates all nodes in the tree. +/// @param tree The tree to deallocate. +/// @param node_cb Optional callback to call on each node before deallocation. +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb); + +/// @brief Searches for a node in the tree based on a given value. +/// @param tree The tree to search in. +/// @param value The value to search for. +/// @return Pointer to the found node, or NULL if not found. +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, void *value); + +/// @brief Removes a specified node from the tree with an optional callback. +/// @param tree The tree containing the node. +/// @param node The node to remove. +/// @param node_cb Optional callback to call before removing the node. +/// @return 1 if the node was removed, 0 otherwise. +int ndtree_tree_remove_node(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb); + +/// @brief Initiates a recursive visit through all nodes in the tree, calling specified enter and exit functions. +/// @param tree The tree to traverse. +/// @param enter_fun Function to call upon entering each node, or NULL to skip. +/// @param exit_fun Function to call upon exiting each node, or NULL to skip. +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun); diff --git a/libc/inc/strerror.h b/libc/inc/strerror.h index bc9b1b6c..788a5262 100644 --- a/libc/inc/strerror.h +++ b/libc/inc/strerror.h @@ -5,7 +5,7 @@ #pragma once -#include +#include /// @brief Returns the string representing the error number. /// @param errnum The error number. diff --git a/libc/inc/sys/ioctl.h b/libc/inc/sys/ioctl.h index 829cffaa..a700df9d 100644 --- a/libc/inc/sys/ioctl.h +++ b/libc/inc/sys/ioctl.h @@ -5,10 +5,9 @@ #pragma once -/// @brief Perform the I/O control operation specified by REQUEST on FD. -/// One argument may follow; its presence and type depend on REQUEST. -/// @param fd Must be an open file descriptor. -/// @param request The device-dependent request code -/// @param data An untyped pointer to memory. -/// @return Return value depends on REQUEST. Usually -1 indicates error. -int ioctl(int fd, unsigned long int request, void *data); +/// @brief Executes a device-specific control operation on a file descriptor. +/// @param fd The file descriptor for the device or file being operated on. +/// @param request The `ioctl` command, defining the action or configuration. +/// @param data Additional data needed for the `ioctl` command, often a pointer to user-provided data. +/// @return Returns 0 on success; on error, returns a negative error code. +long ioctl(int fd, unsigned int request, unsigned long data); diff --git a/libc/inc/sys/ipc.h b/libc/inc/sys/ipc.h index 61583f4b..ee2aaf9d 100644 --- a/libc/inc/sys/ipc.h +++ b/libc/inc/sys/ipc.h @@ -40,4 +40,4 @@ struct ipc_perm { /// @param path The file path. /// @param id the project id. /// @return the IPC key. -key_t ftok(char *path, int id); +key_t ftok(const char *path, int id); diff --git a/libc/inc/sys/mman.h b/libc/inc/sys/mman.h index 5d33fdd3..7c08ee4a 100644 --- a/libc/inc/sys/mman.h +++ b/libc/inc/sys/mman.h @@ -14,6 +14,8 @@ #define MAP_SHARED 0x01 ///< The memory is shared. #define MAP_PRIVATE 0x02 ///< The memory is private. +#if 0 + /// @brief creates a new mapping in the virtual address space of the calling process. /// @param addr the starting address for the new mapping. /// @param length specifies the length of the mapping (which must be greater than 0). @@ -29,3 +31,5 @@ void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) /// @param length the length of the mapped area. /// @return 0 on success, -1 on falure and errno is set. int munmap(void *addr, size_t length); + +#endif diff --git a/libc/inc/sys/types.h b/libc/inc/sys/types.h index c974e615..b72bb23e 100644 --- a/libc/inc/sys/types.h +++ b/libc/inc/sys/types.h @@ -5,17 +5,20 @@ #pragma once -/// The type of process id. +/// @brief The type of process id. typedef signed int pid_t; - -/// The type of process user variable. +/// @brief The type of process user variable. typedef unsigned int user_t; - -/// The type of process status. +/// @brief The type of process status. typedef unsigned int status_t; - -/// Type for system keys. +/// @brief Type for system keys. typedef int key_t; +/// @brief Represents the number of hard links to a file. +typedef unsigned int nlink_t; +/// @brief Represents the preferred block size for filesystem I/O. +typedef long blksize_t; +/// @brief Represents the number of 512B blocks allocated to a file. +typedef long blkcnt_t; /// Defines the list of flags of a process. typedef enum eflags_list { diff --git a/libc/inc/sys/wait.h b/libc/inc/sys/wait.h index ec491973..b8cb03f4 100644 --- a/libc/inc/sys/wait.h +++ b/libc/inc/sys/wait.h @@ -19,19 +19,19 @@ /// @brief returns true if the child process that caused the return is /// currently stopped; this is only possible if the call was done using /// WUNTRACED(). -#define WIFSTOPPED(status) (((status)&0xff) == 0x7f) +#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) /// @brief evaluates to the least significant eight bits of the return code /// of the child that terminated, which may have been set as the argument /// to a call to exit() or as the argument for a return statement in the /// main program. This macro can only be evaluated if WIFEXITED() /// returned nonzero. -#define WEXITSTATUS(status) (((status)&0xff00) >> 8) +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) /// @brief returns the number of the signal that caused the child process to /// terminate. This macro can only be evaluated if WIFSIGNALED() returned /// nonzero. -#define WTERMSIG(status) ((status)&0x7f) +#define WTERMSIG(status) ((status) & 0x7f) /// @brief Is nonzero if the child exited normally. #define WIFEXITED(status) (WTERMSIG(status) == 0) diff --git a/libc/inc/syslog.h b/libc/inc/syslog.h new file mode 100644 index 00000000..e303fa21 --- /dev/null +++ b/libc/inc/syslog.h @@ -0,0 +1,77 @@ +/// @file printk.c +/// @brief Functions for kernel log messages. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "string.h" + +// Log levels for setting the severity of log messages. +#define LOG_EMERG 0 ///< Emergency: system unusable. +#define LOG_ALERT 1 ///< Alert: immediate action required. +#define LOG_CRIT 2 ///< Critical: critical conditions. +#define LOG_ERR 3 ///< Error: error conditions. +#define LOG_WARNING 4 ///< Warning: warning conditions. +#define LOG_NOTICE 5 ///< Notice: significant condition. +#define LOG_INFO 6 ///< Info: informational messages. +#define LOG_DEBUG 7 ///< Debug: debugging messages. + +// Option flags for syslog behavior. +#define LOG_CONS 0x01 ///< Log to console if there are issues with logging +#define LOG_PID 0x02 ///< Include the process ID with each log message + +// Log facilities. +#define LOG_KERN (0 << 3) ///< Kernel messages +#define LOG_USER (1 << 3) ///< User-level messages +#define LOG_MAIL (2 << 3) ///< Mail system +#define LOG_DAEMON (3 << 3) ///< System daemons +#define LOG_AUTH (4 << 3) ///< Security/authorization messages +#define LOG_SYSLOG (5 << 3) ///< Messages generated internally by syslogd +#define LOG_LPR (6 << 3) ///< Printer subsystem +#define LOG_NEWS (7 << 3) ///< Network news subsystem +#define LOG_UUCP (8 << 3) ///< UUCP subsystem +#define LOG_CRON (9 << 3) ///< Clock daemon (cron and at) +#define LOG_AUTHPRIV (10 << 3) ///< Security/authorization (private) +#define LOG_FTP (11 << 3) ///< FTP daemon + +/// @brief Creates a log mask that includes all priorities up to the specified level. +#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1) + +/// @brief Opens a connection to the system log +/// @param ident Identifier string for log messages +/// @param option Flags for logging options, e.g., LOG_PID to include the process ID +/// @param facility The log facility, e.g., LOG_USER for user-level messages +void openlog(const char *ident, int option, int facility); + +/// @brief Sets the log level mask to control which messages are logged +/// @param mask Bitmask for allowed log levels; use LOG_UPTO to set a maximum level +/// @return The previous log mask +int setlogmask(int mask); + +/// @brief Closes the syslog connection and resets log settings +void closelog(void); + +/// @brief Sends a formatted message to the system log +/// @param file the name of the file. +/// @param fun the name of the function. +/// @param line the line inside the file. +/// @param log_level the log level. +/// @param format the format to used, see printf. +/// @param ... Arguments for the format string +/// @return The number of bytes written or -1 on failure +int __syslog(const char *file, const char *fun, int line, short log_level, const char *format, ...); + +/// @brief Extracts the relative path of the current file from the project root. +/// @details This macro calculates the relative path of the file (`__FILE__`) by +/// skipping the prefix defined by `MENTOS_ROOT`. It is used to simplify file +/// path logging by removing the absolute path up to the project root. +/// If +/// MENTOS_ROOT = "/path/to/mentos" and +/// __FILE__ = "/path/to/mentos/src/kernel/main.c", the result will be +/// "src/kernel/main.c". +#define __RELATIVE_PATH__ \ + (strncmp(__FILE__, MENTOS_ROOT, sizeof(MENTOS_ROOT) - 1) == 0 ? (&__FILE__[sizeof(MENTOS_ROOT)]) : __FILE__) + +/// @brief Wrapper macro to simplify usage. +#define syslog(...) __syslog(__RELATIVE_PATH__, __func__, __LINE__, __VA_ARGS__) diff --git a/libc/inc/system/syscall_types.h b/libc/inc/system/syscall_types.h index 1cadebb9..ed6efc76 100644 --- a/libc/inc/system/syscall_types.h +++ b/libc/inc/system/syscall_types.h @@ -400,113 +400,43 @@ // /// @brief Heart of the code that calls a system call with 0 parameters. -#define __inline_syscall0(res, name) \ - __asm__ __volatile__("int $0x80" \ - : "=a"(res) \ +#define __inline_syscall_0(res, name) \ + __asm__ __volatile__("int $0x80" \ + : "=a"(res) \ : "0"(__NR_##name)) /// @brief Heart of the code that calls a system call with 1 parameter. -#define __inline_syscall1(res, name, arg1) \ +#define __inline_syscall_1(res, name, arg1) \ __asm__ __volatile__("push %%ebx; movl %2,%%ebx; int $0x80; pop %%ebx" \ : "=a"(res) \ : "0"(__NR_##name), "ri"(arg1) \ : "memory"); /// @brief Heart of the code that calls a system call with 2 parameters. -#define __inline_syscall2(res, name, arg1, arg2) \ +#define __inline_syscall_2(res, name, arg1, arg2) \ __asm__ __volatile__("push %%ebx; movl %2,%%ebx; int $0x80; pop %%ebx" \ : "=a"(res) \ : "0"(__NR_##name), "ri"(arg1), "c"(arg2) \ : "memory"); /// @brief Heart of the code that calls a system call with 3 parameters. -#define __inline_syscall3(res, name, arg1, arg2, arg3) \ +#define __inline_syscall_3(res, name, arg1, arg2, arg3) \ __asm__ __volatile__("push %%ebx; movl %2,%%ebx; int $0x80; pop %%ebx" \ : "=a"(res) \ : "0"(__NR_##name), "ri"(arg1), "c"(arg2), "d"(arg3) \ : "memory"); /// @brief Heart of the code that calls a system call with 4 parameters. -#define __inline_syscall4(res, name, arg1, arg2, arg3, arg4) \ +#define __inline_syscall_4(res, name, arg1, arg2, arg3, arg4) \ __asm__ __volatile__("push %%ebx; movl %2,%%ebx; int $0x80; pop %%ebx" \ : "=a"(res) \ : "0"(__NR_##name), "ri"(arg1), "c"(arg2), "d"(arg3), "S"(arg4) \ : "memory"); /// @brief Heart of the code that calls a system call with 5 parameters. -#define __inline_syscall5(res, name, arg1, arg2, arg3, arg4, arg5) \ +#define __inline_syscall_5(res, name, arg1, arg2, arg3, arg4, arg5) \ __asm__ __volatile__("push %%ebx; movl %2,%%ebx; movl %1,%%eax; " \ "int $0x80; pop %%ebx" \ : "=a"(res) \ : "i"(__NR_##name), "ri"(arg1), "c"(arg2), "d"(arg3), "S"(arg4), "D"(arg5) \ : "memory"); - -/// @brief System call with 0 parameters. -#define _syscall0(type, name) \ - type name(void) \ - { \ - long __res; \ - __inline_syscall0(__res, name); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 1 parameter. -#define _syscall1(type, name, type1, arg1) \ - type name(type1 arg1) \ - { \ - long __res; \ - __inline_syscall1(__res, name, arg1); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 2 parameters. -#define _syscall2(type, name, type1, arg1, type2, arg2) \ - type name(type1 arg1, type2 arg2) \ - { \ - long __res; \ - __inline_syscall2(__res, name, arg1, arg2); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 3 parameters. -#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ - type name(type1 arg1, type2 arg2, type3 arg3) \ - { \ - long __res; \ - __inline_syscall3(__res, name, arg1, arg2, arg3); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 4 parameters. -#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ - type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ - { \ - long __res; \ - __inline_syscall4(__res, name, arg1, arg2, arg3, arg4); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 5 parameters. -#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ - type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ - { \ - long __res; \ - __inline_syscall5(__res, name, arg1, arg2, arg3, arg4, arg5); \ - __syscall_return(type, __res); \ - } - -/// @brief System call with 5 parameters. -#define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6) \ - type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) \ - { \ - long __res; \ - unsigned args[6] = { 0 }; \ - args[0] = (unsigned)arg1; \ - args[1] = (unsigned)arg2; \ - args[2] = (unsigned)arg3; \ - args[3] = (unsigned)arg4; \ - args[4] = (unsigned)arg5; \ - args[5] = (unsigned)arg6; \ - __inline_syscall1(__res, name, args); \ - __syscall_return(type, __res); \ - } diff --git a/libc/inc/time.h b/libc/inc/time.h index cdfadce9..1f4b602e 100644 --- a/libc/inc/time.h +++ b/libc/inc/time.h @@ -61,9 +61,9 @@ typedef struct timespec { long tv_nsec; ///< Nanoseconds. } timespec_t; -/// @brief Returns the current time. -/// @param t Where the time should be stored. -/// @return The current time. +/// @brief Retrieves the current time. +/// @param t Pointer to a `time_t` variable to store the current time, or NULL if not needed. +/// @return The current time as `time_t`, or (time_t)-1 on failure. time_t time(time_t *t); /// @brief Converts the given time to a string representing the local time. @@ -109,14 +109,6 @@ size_t strftime(char *s, size_t max, const char *format, const tm_t *tm); /// the process. int nanosleep(const struct timespec *req, struct timespec *rem); -/// @brief Causes the calling thread to sleep either until the number of -/// real-time seconds specified in seconds have elapsed or -/// until a signal arrives which is not ignored. -/// @param seconds The number of seconds we want to sleep. -/// @return Zero if the requested time has elapsed, or the number of seconds -/// left to sleep, if the call was interrupted by a signal handler. -unsigned int sleep(unsigned int seconds); - /// @brief Fills the structure pointed to by curr_value with the current setting /// for the timer specified by which. /// @param which which timer. diff --git a/libc/inc/unistd.h b/libc/inc/unistd.h index 4ce6c567..78bef518 100644 --- a/libc/inc/unistd.h +++ b/libc/inc/unistd.h @@ -75,25 +75,25 @@ int readlink(const char *path, char *buffer, size_t bufsize); /// @return pid_t process identifier. pid_t getpid(void); -///@brief Return session id of the given process. +/// @brief Return session id of the given process. /// If pid == 0 return the SID of the calling process /// If pid != 0 return the SID corresponding to the process having identifier == pid -///@param pid process identifier from wich we want the SID -///@return On success return SID of the session +/// @param pid process identifier from wich we want the SID +/// @return On success return SID of the session /// Otherwise return -1 with errno set on: EPERM or ESRCH pid_t getsid(pid_t pid); -///@brief creates a new session if the calling process is not a +/// @brief creates a new session if the calling process is not a /// process group leader. The calling process is the leader of the /// new session (i.e., its session ID is made the same as its process /// ID). The calling process also becomes the process group leader /// of a new process group in the session (i.e., its process group ID /// is made the same as its process ID). -///@return On success return SID of the session just created +/// @return On success return SID of the session just created /// Otherwise return -1 with errno : EPERM pid_t setsid(void); -///@brief returns the Process Group ID (PGID) of the process specified by pid. +/// @brief returns the Process Group ID (PGID) of the process specified by pid. /// If pid is zero, the process ID of the calling process is used. /// @param pid process of which we want to know the PGID. /// @return the PGID of the specified process. @@ -106,45 +106,44 @@ pid_t getpgid(pid_t pid); /// @return returns zero. On error, -1 is returned, and errno is set appropriately. int setpgid(pid_t pid, pid_t pgid); -///@brief returns the real group ID of the calling process. -///@return GID of the current process +/// @brief returns the real group ID of the calling process. +/// @return GID of the current process gid_t getgid(void); -///@brief returns the effective group ID of the calling process. -///@return GID of the current process +/// @brief returns the effective group ID of the calling process. +/// @return GID of the current process gid_t getegid(void); -///@brief sets the group IDs of the calling process. -///@param gid the Group ID to set -///@return On success, zero is returned. -/// Otherwise returns -1 with errno set to :EINVAL or EPERM +/// @brief sets the group IDs of the calling process. +/// @param gid the Group ID to set +/// @return On success, zero is returned. Otherwise returns -1 with errno. int setgid(gid_t gid); -///@brief sets the real and effective group IDs of the calling process. -///@param rgid the new real Group ID. -///@param egid the effective real Group ID. -///@return On success, zero is returned. +/// @brief sets the real and effective group IDs of the calling process. +/// @param rgid the new real Group ID. +/// @param egid the effective real Group ID. +/// @return On success, zero is returned. /// Otherwise returns -1 with errno set EPERM int setregid(gid_t rgid, gid_t egid); -///@brief Returns the real User ID of the calling process. -///@return User ID of the current process. +/// @brief Returns the real User ID of the calling process. +/// @return User ID of the current process. uid_t getuid(void); -///@brief Returns the effective User ID of the calling process. -///@return User ID of the current process. +/// @brief Returns the effective User ID of the calling process. +/// @return User ID of the current process. uid_t geteuid(void); -///@brief Sets the User IDs of the calling process. -///@param uid the new User ID. -///@return On success, zero is returned. +/// @brief Sets the User IDs of the calling process. +/// @param uid the new User ID. +/// @return On success, zero is returned. /// Otherwise returns -1 with errno set to :EINVAL or EPERM int setuid(uid_t uid); -///@brief Sets the effective and real User IDs of the calling process. -///@param ruid the new real User ID. -///@param euid the effective real User ID. -///@return On success, zero is returned. +/// @brief Sets the effective and real User IDs of the calling process. +/// @param ruid the new real User ID. +/// @param euid the effective real User ID. +/// @return On success, zero is returned. /// Otherwise returns -1 with errno set to EPERM int setreuid(uid_t ruid, uid_t euid); @@ -306,3 +305,16 @@ int fchown(int fd, uid_t owner, gid_t group); /// @return On success, 0 is returned. /// On error, -1 is returned, and errno is set appropriately. int lchown(const char *pathname, uid_t owner, gid_t group); + +/// @brief Causes the calling thread to sleep either until the number of +/// real-time seconds specified in seconds have elapsed or +/// until a signal arrives which is not ignored. +/// @param seconds The number of seconds we want to sleep. +/// @return Zero if the requested time has elapsed, or the number of seconds +/// left to sleep, if the call was interrupted by a signal handler. +unsigned int sleep(unsigned int seconds); + +/// @brief System call to create a new pipe. +/// @param fds Array to store read and write file descriptors. +/// @return 0 on success, or -1 on error. +int pipe(int fds[2]); diff --git a/libc/src/assert.c b/libc/src/assert.c index 6c763c03..0db2ef65 100644 --- a/libc/src/assert.c +++ b/libc/src/assert.c @@ -9,10 +9,12 @@ void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) { - printf("FILE: %s\n" - "FUNC: %s\n" - "LINE: %d\n\n" - "Assertion `%s` failed.\n", - file, (function ? function : "NO_FUN"), line, assertion); + printf("\n=== ASSERTION FAILED ===\n" + "Assertion: %s\n" + "Location : %s:%d\n" + "Function : %s\n\n", + assertion, + file, line, + (function ? function : "Unknown function")); abort(); } diff --git a/libc/src/crypt/sha256.c b/libc/src/crypt/sha256.c index 9addd98c..cf9d5b15 100644 --- a/libc/src/crypt/sha256.c +++ b/libc/src/crypt/sha256.c @@ -10,16 +10,11 @@ /// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf /// This implementation uses little endian byte order. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SHA256]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include "crypt/sha256.h" #include #include +#include /// @brief Rotate left operation on a 32-bit unsigned integer. /// @param a The value to rotate. @@ -90,11 +85,11 @@ static inline void __sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) { // Error checks: Ensure the input parameters are not NULL. if (!ctx) { - pr_err("SHA256 context is NULL.\n"); + perror("SHA256 context is NULL.\n"); return; } if (!data) { - pr_err("Input data is NULL.\n"); + perror("Input data is NULL.\n"); return; } @@ -161,7 +156,7 @@ void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_ { // Check if the output buffer is large enough to hold the hex string if (out_length < (src_length * 2 + 1)) { - pr_err("Output buffer is too small for the hex representation.\n"); + perror("Output buffer is too small for the hex representation.\n"); return; // Return early if the buffer is insufficient. } @@ -181,7 +176,7 @@ void sha256_init(SHA256_ctx_t *ctx) { // Error check: Ensure the input context is not NULL. if (!ctx) { - pr_err("SHA256 context is NULL.\n"); + perror("SHA256 context is NULL.\n"); return; // Return early if the context is NULL to prevent crashes. } @@ -206,12 +201,12 @@ void sha256_update(SHA256_ctx_t *ctx, const uint8_t data[], size_t len) { // Error check: Ensure the input context and data are not NULL. if (!ctx) { - pr_err("SHA256 context is NULL.\n"); + perror("SHA256 context is NULL.\n"); return; // Return early if the context is NULL to prevent crashes. } if (!data) { - pr_err("Input data is NULL.\n"); + perror("Input data is NULL.\n"); return; // Return early if the data is NULL to prevent errors. } @@ -239,11 +234,11 @@ void sha256_final(SHA256_ctx_t *ctx, uint8_t hash[]) { // Error check: Ensure the input context and hash are not NULL. if (!ctx) { - pr_err("SHA256 context is NULL.\n"); + perror("SHA256 context is NULL.\n"); return; // Return early if the context is NULL to prevent crashes. } if (!hash) { - pr_err("Output hash buffer is NULL.\n"); + perror("Output hash buffer is NULL.\n"); return; // Return early if the output buffer is NULL to prevent errors. } diff --git a/libc/src/grp.c b/libc/src/grp.c index e1e8f37c..4a2731fd 100644 --- a/libc/src/grp.c +++ b/libc/src/grp.c @@ -6,10 +6,9 @@ #include "grp.h" #include "assert.h" #include "fcntl.h" -#include "io/debug.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "unistd.h" /// Holds the file descriptor while we are working with `/etc/group`. diff --git a/libc/src/hashmap.c b/libc/src/hashmap.c new file mode 100644 index 00000000..5dbf8b73 --- /dev/null +++ b/libc/src/hashmap.c @@ -0,0 +1,100 @@ +/// @file hashmap.c +/// @brief Source file for a hashmap implementation with `char *` keys. + +#include "hashmap.h" + +#include +#include +#include + +size_t hash(const char *key) +{ + size_t hash = 5381; + int c; + while ((c = *key++)) { + hash = ((hash << 5) + hash) + c; // hash * 33 + c + } + return hash % HASHMAP_SIZE; +} + +void hashmap_init(hashmap_t *map, hashmap_entry_t *(*alloc_fn)(void), void (*dealloc_fn)(hashmap_entry_t *)) +{ + assert(map && "Hashmap is NULL."); + memset(map->buckets, 0, sizeof(map->buckets)); + map->alloc_entry = alloc_fn; + map->dealloc_entry = dealloc_fn; +} + +void hashmap_insert(hashmap_t *map, const char *key, void *value) +{ + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); + + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *new_entry = map->alloc_entry(); + assert(new_entry && "Failed to allocate memory for hashmap entry."); + + new_entry->hash = hashed_key; + new_entry->value = value; + new_entry->next = map->buckets[index]; + map->buckets[index] = new_entry; +} + +void *hashmap_get(hashmap_t *map, const char *key) +{ + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); + + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *entry = map->buckets[index]; + + while (entry != NULL) { + if (entry->hash == hashed_key) { + return entry->value; + } + entry = entry->next; + } + return NULL; // Key not found +} + +void hashmap_remove(hashmap_t *map, const char *key) +{ + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); + + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *entry = map->buckets[index]; + hashmap_entry_t *prev = NULL; + + while (entry != NULL) { + if (entry->hash == hashed_key) { + if (prev == NULL) { + map->buckets[index] = entry->next; + } else { + prev->next = entry->next; + } + map->dealloc_entry(entry); + return; + } + prev = entry; + entry = entry->next; + } +} + +void hashmap_destroy(hashmap_t *map) +{ + assert(map && "Hashmap is NULL."); + + for (int i = 0; i < HASHMAP_SIZE; i++) { + hashmap_entry_t *entry = map->buckets[i]; + while (entry != NULL) { + hashmap_entry_t *next_entry = entry->next; + map->dealloc_entry(entry); + entry = next_entry; + } + map->buckets[i] = NULL; + } +} diff --git a/libc/src/io/debug.c b/libc/src/io/debug.c deleted file mode 100644 index 29649cb6..00000000 --- a/libc/src/io/debug.c +++ /dev/null @@ -1,196 +0,0 @@ -/// @file debug.c -/// @brief Debugging primitives. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "io/debug.h" -#include "io/ansi_colors.h" -#include "io/port_io.h" -#include "math.h" -#include "stdio.h" -#include "string.h" -#include "sys/bitops.h" - -/// Serial port for QEMU. -#define SERIAL_COM1 (0x03F8) -/// Determines the log level. -static int max_log_level = LOGLEVEL_DEBUG; - -/// @brief Prints the correct header for the given debug level. -/// @param file the file origin of the debug message. -/// @param fun the function where the debug message was called. -/// @param line the line in the file where debug message was called. -/// @param log_level the log level. -/// @param header the header we want to show. -static inline void __debug_print_header(const char *file, const char *fun, int line, short log_level, char *header) -{ - // "EMERG ", "ALERT ", "CRIT ", "ERR ", "WARNING", "NOTICE ", "INFO ", "DEBUG ", "DEFAULT", - static const char *log_level_label[] = { " EM ", " AL ", " CR ", " ER ", " WR ", " NT ", " IN ", " DB ", " DF " }; - static const char *log_level_color[] = { - FG_RED_BRIGHT, // "EMERG " - FG_RED_BRIGHT, // "ALERT " - FG_RED, // "CRIT " - FG_RED, // "ERR " - FG_YELLOW_BRIGHT, // "WARNING" - FG_RESET, // "NOTICE " - FG_CYAN, // "INFO " - FG_YELLOW, // "DEBUG " - FG_RESET // "DEFAULT" - }; - static char tmp_prefix[BUFSIZ], final_prefix[BUFSIZ]; - // Check the log level. - if ((log_level < LOGLEVEL_EMERG) || (log_level > LOGLEVEL_DEBUG)) { - // Set it to default. - log_level = 8; - } - // Set the color. -#ifndef EMULATOR_OUTPUT_LOG - dbg_puts(log_level_color[log_level]); -#endif - dbg_putchar('['); - // Set the label. - dbg_puts(log_level_label[log_level]); - dbg_putchar('|'); - // Print the file and line. - sprintf(tmp_prefix, "%s:%d", file, line); - // Print the message. - sprintf(final_prefix, " %-20s ", tmp_prefix); - // Print the actual message. - dbg_puts(final_prefix); -#if 0 - dbg_putchar('|'); - sprintf(final_prefix, " %-25s ]", fun); - dbg_puts(final_prefix); -#else - dbg_putchar(']'); -#endif - dbg_putchar(' '); - if (header) { - dbg_puts(header); - dbg_putchar(' '); - } -} - -void dbg_putchar(char c) -{ - outportb(SERIAL_COM1, (unsigned char)c); -} - -void dbg_puts(const char *s) -{ - while ((*s) != 0) { - dbg_putchar(*s++); - } -} - -void set_log_level(int level) -{ - if ((level >= LOGLEVEL_EMERG) && (level <= LOGLEVEL_DEBUG)) { - max_log_level = level; - } -} - -int get_log_level(void) -{ - return max_log_level; -} - -void dbg_printf(const char *file, const char *fun, int line, char *header, short log_level, const char *format, ...) -{ - // Define a buffer for the formatted string. - static char formatted[BUFSIZ]; - static short new_line = 1; - - // Stage 1: FORMAT - if (strlen(format) >= BUFSIZ) { - return; - } - - // Start variabile argument's list. - va_list ap; - va_start(ap, format); - // Format the message. - vsprintf(formatted, format, ap); - // End the list of arguments. - va_end(ap); - - // Stage 2: SEND - if (new_line) { - __debug_print_header(file, fun, line, log_level, header); - new_line = 0; - } - for (int it = 0; (formatted[it] != 0) && (it < BUFSIZ); ++it) { - dbg_putchar(formatted[it]); - if (formatted[it] != '\n') { - continue; - } - if ((it + 1) >= BUFSIZ) { - continue; - } - if (formatted[it + 1] == 0) { - new_line = 1; - } else { - __debug_print_header(file, fun, line, log_level, header); - } - } -} - -const char *to_human_size(unsigned long bytes) -{ - static char output[200]; - const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; - char length = sizeof(suffix) / sizeof(suffix[0]); - int i = 0; - double dblBytes = bytes; - if (bytes > 1024) { - for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) { - dblBytes = bytes / 1024.0; - } - } - sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); - return output; -} - -const char *dec_to_binary(unsigned long value, unsigned length) -{ - static char buffer[33]; - // Adjust the length. - length = min(max(0, length), 32U); - // Build the binary. - for (unsigned i = 0, j = 32U - length; j < 32U; ++i, ++j) { - buffer[i] = bit_check(value, 31 - j) ? '1' : '0'; - } - // Close the string. - buffer[length] = 0; - return buffer; -} - -#ifdef __KERNEL__ - -#include "kernel.h" - -void dbg_print_regs(pt_regs *frame) -{ - pr_debug("Interrupt stack frame:\n"); - pr_debug("GS = 0x%-04x\n", frame->gs); - pr_debug("FS = 0x%-04x\n", frame->fs); - pr_debug("ES = 0x%-04x\n", frame->es); - pr_debug("DS = 0x%-04x\n", frame->ds); - pr_debug("EDI = 0x%-09x\n", frame->edi); - pr_debug("ESI = 0x%-09x\n", frame->esi); - pr_debug("EBP = 0x%-09x\n", frame->ebp); - pr_debug("ESP = 0x%-09x\n", frame->esp); - pr_debug("EBX = 0x%-09x\n", frame->ebx); - pr_debug("EDX = 0x%-09x\n", frame->edx); - pr_debug("ECX = 0x%-09x\n", frame->ecx); - pr_debug("EAX = 0x%-09x\n", frame->eax); - pr_debug("INT_NO = %-9d\n", frame->int_no); - pr_debug("ERR_CD = %-9d\n", frame->err_code); - pr_debug("EIP = 0x%-09x\n", frame->eip); - pr_debug("CS = 0x%-04x\n", frame->cs); - pr_debug("EFLAGS = 0x%-09x\n", frame->eflags); - pr_debug("UESP = 0x%-09x\n", frame->useresp); - pr_debug("SS = 0x%-04x\n", frame->ss); -} - -#endif diff --git a/libc/src/libc_start.c b/libc/src/libc_start.c index 131a9587..488731a9 100644 --- a/libc/src/libc_start.c +++ b/libc/src/libc_start.c @@ -7,7 +7,7 @@ #include "assert.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" /// @brief Reference to the `environ` variable in `setenv.c`. diff --git a/libc/src/libgen.c b/libc/src/libgen.c index 72be3cc9..cb017a6e 100644 --- a/libc/src/libgen.c +++ b/libc/src/libgen.c @@ -4,7 +4,6 @@ /// See LICENSE.md for details. #include "libgen.h" -#include "io/debug.h" #include "limits.h" #include "string.h" #include diff --git a/libc/src/list.c b/libc/src/list.c new file mode 100644 index 00000000..61666135 --- /dev/null +++ b/libc/src/list.c @@ -0,0 +1,135 @@ +/// @file list.c +/// @brief Source file for list operations, implementing list manipulation functions. + +#include "list.h" + +void list_init(list_t *list, listnode_t *(*alloc_fn)(void), void (*dealloc_fn)(listnode_t *)) +{ + assert(list && "List is null."); + list_head_init(&list->head); + list->size = 0; + list->alloc = alloc_fn; + list->dealloc = dealloc_fn; +} + +listnode_t *list_insert_front(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + listnode_t *node = list->alloc(); + assert(node && "Failed to allocate node."); + node->value = value; + list_head_insert_after(&node->list, &list->head); + list->size++; + return node; +} + +listnode_t *list_insert_back(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + listnode_t *node = list->alloc(); + assert(node && "Failed to allocate node."); + node->value = value; + list_head_insert_before(&node->list, &list->head); + list->size++; + return node; +} + +void *list_remove_node(list_t *list, listnode_t *node) +{ + assert(list && "List is null."); + assert(node && "Node is null."); + void *value = node->value; + list_head_remove(&node->list); + list->dealloc(node); + list->size--; + return value; +} + +void *list_remove_front(list_t *list) +{ + assert(list && "List is null."); + if (list_head_empty(&list->head)) { + return NULL; + } + listnode_t *node = list_entry(list->head.next, listnode_t, list); + return list_remove_node(list, node); +} + +void *list_remove_back(list_t *list) +{ + assert(list && "List is null."); + if (list_head_empty(&list->head)) { + return NULL; + } + listnode_t *node = list_entry(list->head.prev, listnode_t, list); + return list_remove_node(list, node); +} + +void list_destroy(list_t *list) +{ + assert(list && "List is null."); + list_for_each_safe_decl(entry, store, &list->head) + { + listnode_t *it = list_entry(entry, listnode_t, list); + list->dealloc(it); + } + list_head_init(&list->head); + list->size = 0; +} + +listnode_t *list_find(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + list_for_each_safe_decl(entry, store, &list->head) + { + listnode_t *it = list_entry(entry, listnode_t, list); + if (it->value == value) { + return it; + } + } + return NULL; +} + +void *list_peek_front(const list_t *list) +{ + assert(list && "List is null."); + if (list_empty(list)) { + return NULL; + } + listnode_t *front_node = list_entry(list->head.next, listnode_t, list); + return front_node->value; +} + +void *list_peek_back(const list_t *list) +{ + assert(list && "List is null."); + if (list_empty(list)) { + return NULL; + } + listnode_t *back_node = list_entry(list->head.prev, listnode_t, list); + return back_node->value; +} + +void list_merge(list_t *target, list_t *source) +{ + assert(target && "Target list is null."); + assert(source && "Source list is null."); + if (!list_empty(source)) { + listnode_t *target_last = list_entry(target->head.prev, listnode_t, list); + listnode_t *source_first = list_entry(source->head.next, listnode_t, list); + + target_last->list.next = &source_first->list; + source_first->list.prev = &target_last->list; + + listnode_t *source_last = list_entry(source->head.prev, listnode_t, list); + source_last->list.next = &target->head; + target->head.prev = &source_last->list; + + target->size += source->size; + list_head_init(&source->head); + source->size = 0; + } +} diff --git a/libc/src/ndtree.c b/libc/src/ndtree.c new file mode 100644 index 00000000..dc23697c --- /dev/null +++ b/libc/src/ndtree.c @@ -0,0 +1,294 @@ +/// @file ndtree.c +/// @brief Red/Black tree. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ndtree.h" + +#include "assert.h" + +// ============================================================================ +// Init functions. + +void ndtree_tree_init(ndtree_t *tree, ndtree_tree_compare_f compare_node, ndtree_alloc_node_f alloc_node, ndtree_free_node_f free_node) +{ + // Validate that the tree and function pointers are not NULL. + assert(tree && "ndtree_tree_init: Variable tree is NULL."); + assert(compare_node && "ndtree_tree_init: Function pointer compare_node is NULL."); + assert(alloc_node && "ndtree_tree_init: Function pointer alloc_node is NULL."); + assert(free_node && "ndtree_tree_init: Function pointer free_node is NULL."); + + // Initialize tree properties to default values. + tree->size = 0; + tree->root = NULL; + tree->compare_node = compare_node; + tree->alloc_node = alloc_node; + tree->free_node = free_node; + + // Initialize the orphan list head. + list_head_init(&tree->orphans); +} + +void ndtree_node_init(ndtree_node_t *node, void *value) +{ + // Validate that the node and value are not NULL. + assert(node && "ndtree_node_init: Variable node is NULL."); + assert(value && "ndtree_node_init: Variable value is NULL."); + + // Set the node's value and initialize relationships. + node->value = value; + node->parent = NULL; + + // Initialize sibling and children lists. + list_head_init(&node->siblings); + list_head_init(&node->children); +} + +// ============================================================================ +// Node Management Functions + +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value) +{ + // Ensure the tree and the allocation function are valid. + assert(tree && "ndtree_create_root: Variable tree is NULL."); + assert(value && "ndtree_create_root: Variable value is NULL."); + + // Allocate a new node for the root using the custom allocator. + ndtree_node_t *node = tree->alloc_node(value); + if (node) { + // Initialize the node with the provided value. + ndtree_node_init(node, value); + + // Set this node as the tree's root and update the tree size. + tree->root = node; + tree->size = 1; + } + + // Return the newly created root node, or NULL if allocation failed. + return node; +} + +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child) +{ + // Ensure the tree, parent, and child nodes are valid. + assert(tree && "ndtree_add_child_to_node: Variable tree is NULL."); + assert(parent && "ndtree_add_child_to_node: Variable parent is NULL."); + assert(child && "ndtree_add_child_to_node: Variable child is NULL."); + + // Set the parent of the child node. + child->parent = parent; + + // Insert the child into the parent's list of children. + list_head_insert_before(&child->siblings, &parent->children); + + // Increment the tree's size to reflect the new node. + ++tree->size; +} + +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value) +{ + // Ensure the tree, allocation function, and parent node are valid. + assert(tree && "ndtree_create_child_of_node: Variable tree is NULL."); + assert(parent && "ndtree_create_child_of_node: Variable parent is NULL."); + assert(value && "ndtree_create_child_of_node: Variable value is NULL."); + + // Allocate a new node for the child using the custom allocator. + ndtree_node_t *child = tree->alloc_node(value); + if (child) { + // Initialize the child node with the provided value. + ndtree_node_init(child, value); + + // Add the child node to the parent node in the tree structure. + ndtree_add_child_to_node(tree, parent, child); + } + + // Return the newly created child node, or NULL if allocation failed. + return child; +} + +unsigned int ndtree_node_count_children(ndtree_node_t *node) +{ + // Ensure the node is valid. + assert(node && "ndtree_node_count_children: Variable node is NULL."); + + // Return the total count of children. + return list_head_size(&node->children); +} + +// ============================================================================ +// Tree Management Functions + +/// @brief Recursively deallocates nodes in the tree. +/// @param tree The tree containing the nodes. +/// @param node The current node to deallocate. +/// @param node_cb Optional callback function to invoke before deallocating each node. +static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + // Ensure the tree, node, and free_node function are valid. + assert(tree && "ndtree_tree_dealloc_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_dealloc_rec: Variable node is NULL."); + + // Iterate safely over the list of children and recursively deallocate each child. + list_for_each_safe_decl(it, store, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + __ndtree_tree_dealloc_rec(tree, child, node_cb); + } + + // Invoke the callback function on the current node, if provided. + if (node_cb) { + node_cb(node); + } + + // Deallocate the current node using the custom free function. + tree->free_node(node); +} + +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb) +{ + // Ensure the tree is valid. + assert(tree && "ndtree_tree_dealloc: Variable tree is NULL."); + + // Check if the tree has a root node to begin deallocation. + if (tree->root) { + // Recursively deallocate all nodes starting from the root. + __ndtree_tree_dealloc_rec(tree, tree->root, node_cb); + + // Reset the tree's root and size after deallocation. + tree->root = NULL; + tree->size = 0; + + // Iterate safely over the list of children and recursively deallocate + // each orphan. + list_for_each_safe_decl(it, store, &tree->orphans) + { + ndtree_node_t *orphan = list_entry(it, ndtree_node_t, siblings); + __ndtree_tree_dealloc_rec(tree, orphan, node_cb); + } + } +} + +// ============================================================================ +// Tree Search Functions + +/// @brief Recursively searches for a node with a specified value in the tree. +/// @param tree The tree to search in. +/// @param value The value to search for. +/// @param node The current node in the recursion. +/// @return Pointer to the found node if successful, NULL otherwise. +static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, void *value, ndtree_node_t *node) +{ + // Ensure the tree, comparison function, node, and search value are valid. + assert(tree && "ndtree_tree_find_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_find_rec: Variable node is NULL."); + assert(value && "ndtree_tree_find_rec: Variable value is NULL."); + + // Check if the current node matches the search value using the comparison function. + if (tree->compare_node(node->value, value) == 0) { + return node; // Return the node if a match is found. + } + + // Recursively search in each child of the current node. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + ndtree_node_t *result = __ndtree_tree_find_rec(tree, value, child); + if (result) { + return result; // Return the matching node if found in recursion. + } + } + + // Return NULL if no match is found in the subtree. + return NULL; +} + +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, void *value) +{ + // Ensure the tree, comparison function, node, and search value are valid. + assert(tree && "ndtree_tree_find: Variable tree is NULL."); + assert(tree->root && "ndtree_tree_find: Variable tree->root is NULL."); + assert(value && "ndtree_tree_find: Variable value is NULL."); + + return __ndtree_tree_find_rec(tree, value, tree->root); +} + +// ============================================================================ +// Tree Removal Functions + +int ndtree_tree_remove_node(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + // Ensure the tree and node are valid. + assert(tree && "ndtree_tree_remove_node: Variable tree is NULL."); + assert(node && "ndtree_tree_remove_node: Variable node is NULL."); + + // Remove the node from its sibling list. + list_head_remove(&node->siblings); + + // If the node has children, orphan them. + if (!list_head_empty(&node->children)) { + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + child->parent = NULL; + } + list_head_append(&tree->orphans, &node->children); + } + + // Call the callback function if provided. + if (node_cb) { + node_cb(node); + } + + // Free the node using the custom deallocator. + tree->free_node(node); + + // Decrement the tree size to reflect the removal. + --tree->size; + + // Return 1 indicating successful removal. + return 1; +} + +// ============================================================================ +// Tree Visit Functions + +/// @brief Recursively visits each node in the tree, calling specified enter and exit functions. +/// @param tree The tree containing the nodes to visit. +/// @param node The current node being visited. +/// @param enter_fun Function to call upon entering each node, or NULL to skip. +/// @param exit_fun Function to call upon exiting each node, or NULL to skip. +static void __ndtree_tree_visitor_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) +{ + // Ensure that the tree and node are valid. + assert(tree && "ndtree_tree_visitor_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_visitor_rec: Variable node is NULL."); + + // Call the enter function, if provided. + if (enter_fun) { + enter_fun(node); + } + + // Recursively visit each child node. + if (!list_head_empty(&node->children)) { + list_for_each_decl(it, &node->children) + { + __ndtree_tree_visitor_rec(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun); + } + } + + // Call the exit function, if provided, after all children are visited. + if (exit_fun) { + exit_fun(node); + } +} + +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) +{ + // Ensure the tree is valid. + assert(tree && "ndtree_tree_visitor: Variable tree is NULL."); + + // Start the recursive visitor from the root node, if it exists. + if (tree->root) { + __ndtree_tree_visitor_rec(tree, tree->root, enter_fun, exit_fun); + } +} diff --git a/libc/src/pwd.c b/libc/src/pwd.c index e67ae9d3..432e5f16 100644 --- a/libc/src/pwd.c +++ b/libc/src/pwd.c @@ -6,11 +6,10 @@ #include "pwd.h" #include "assert.h" #include "fcntl.h" -#include "io/debug.h" #include "readline.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "unistd.h" /// @brief Parses the input buffer and fills pwd with its details. @@ -140,7 +139,7 @@ int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd } int fd = open("/etc/passwd", O_RDONLY, 0); if (fd == -1) { - pr_debug("Cannot open `/etc/passwd`\n"); + perror("Cannot open `/etc/passwd`\n"); errno = ENOENT; *result = NULL; return 0; @@ -165,7 +164,6 @@ int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **re { int fd = open("/etc/passwd", O_RDONLY, 0); if (fd == -1) { - pr_debug("Cannot open `/etc/passwd`\n"); errno = ENOENT; *result = NULL; return 0; diff --git a/libc/src/readline.c b/libc/src/readline.c index f63e378c..da0510ff 100644 --- a/libc/src/readline.c +++ b/libc/src/readline.c @@ -3,12 +3,6 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[RDLINE]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include "readline.h" #include "stdio.h" @@ -19,7 +13,7 @@ int readline(int fd, char *buffer, size_t buflen, ssize_t *read_len) { // Error check: Ensure the buffer is not NULL and has sufficient length. if (!buffer || buflen == 0) { - pr_err("Invalid buffer or buffer length.\n"); + perror("Invalid buffer or buffer length.\n"); return 0; // Invalid input, cannot proceed. } @@ -34,7 +28,7 @@ int readline(int fd, char *buffer, size_t buflen, ssize_t *read_len) // Error check: If read() fails, return -1 to indicate an error. if (num_read < 0) { - pr_err("Failed to read from file descriptor.\n"); + perror("Failed to read from file descriptor.\n"); return -1; } diff --git a/libc/src/sched.c b/libc/src/sched.c index 1006e7d7..c2b54db0 100644 --- a/libc/src/sched.c +++ b/libc/src/sched.c @@ -5,10 +5,28 @@ #include "system/syscall_types.h" #include "sched.h" -#include "sys/errno.h" +#include "errno.h" -_syscall2(int, sched_setparam, pid_t, pid, const sched_param_t *, param) +// _syscall2(int, sched_setparam, pid_t, pid, const sched_param_t *, param) +int sched_setparam(pid_t pid, const sched_param_t *param) +{ + long __res; + __inline_syscall_2(__res, sched_setparam, pid, param); + __syscall_return(int, __res); +} -_syscall2(int, sched_getparam, pid_t, pid, sched_param_t *, param) +// _syscall2(int, sched_getparam, pid_t, pid, sched_param_t *, param) +int sched_getparam(pid_t pid, sched_param_t *param) +{ + long __res; + __inline_syscall_2(__res, sched_getparam, pid, param); + __syscall_return(int, __res); +} -_syscall0(int, waitperiod) +// _syscall0(int, waitperiod) +int waitperiod(void) +{ + long __res; + __inline_syscall_0(__res, waitperiod); + __syscall_return(int, __res); +} diff --git a/libc/src/setenv.c b/libc/src/setenv.c index ca8e4cec..56d534d5 100644 --- a/libc/src/setenv.c +++ b/libc/src/setenv.c @@ -3,7 +3,7 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/errno.h" +#include "errno.h" #include "stdlib.h" #include "string.h" #include diff --git a/libc/src/shadow.c b/libc/src/shadow.c index 49c8fd47..1bec4c3f 100644 --- a/libc/src/shadow.c +++ b/libc/src/shadow.c @@ -8,7 +8,7 @@ #include "stdio.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "unistd.h" #include "sys/stat.h" #include "ctype.h" diff --git a/libc/src/stdio.c b/libc/src/stdio.c index c4aaebca..4c20ed24 100644 --- a/libc/src/stdio.c +++ b/libc/src/stdio.c @@ -3,7 +3,7 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/errno.h" +#include "errno.h" #include "ctype.h" #include "stdbool.h" #include "stdio.h" diff --git a/libc/src/stdlib.c b/libc/src/stdlib.c index 91c87b53..a47aa3ec 100644 --- a/libc/src/stdlib.c +++ b/libc/src/stdlib.c @@ -46,7 +46,7 @@ void *malloc(unsigned int size) } void *ptr; // Allocate the memory. - __inline_syscall1(ptr, brk, size + sizeof(malloc_header_t)); + __inline_syscall_1(ptr, brk, size + sizeof(malloc_header_t)); // Check for errors from the brk. if (ptr == 0) { return NULL; @@ -104,7 +104,7 @@ void free(void *ptr) ptr = (char *)ptr - sizeof(malloc_header_t); // Call the free. int _res; - __inline_syscall1(_res, brk, (char *)header); + __inline_syscall_1(_res, brk, (char *)header); } /// Seed used to generate random numbers. diff --git a/libc/src/strerror.c b/libc/src/strerror.c index 6c1f5748..1091e8af 100644 --- a/libc/src/strerror.c +++ b/libc/src/strerror.c @@ -14,467 +14,270 @@ char *strerror(int errnum) case 0: strcpy(error, "Success"); break; -#ifdef EPERM case EPERM: strcpy(error, "Operation not permitted"); break; -#endif -#ifdef ENOENT case ENOENT: strcpy(error, "No such file or directory"); break; -#endif -#ifdef ESRCH case ESRCH: strcpy(error, "No such process"); break; -#endif -#ifdef EINTR case EINTR: strcpy(error, "Interrupted system call"); break; -#endif -#ifdef EIO case EIO: strcpy(error, "I/O error"); break; -#endif -#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) case ENXIO: strcpy(error, "No such device or address"); break; -#endif -#ifdef E2BIG case E2BIG: - strcpy(error, "Arg list too long"); + strcpy(error, "Argument list too long"); break; -#endif -#ifdef ENOEXEC case ENOEXEC: strcpy(error, "Exec format error"); break; -#endif -#ifdef EALREADY - case EALREADY: - strcpy(error, "Socket already connected"); - break; -#endif -#ifdef EBADF case EBADF: - strcpy(error, "Bad file number"); + strcpy(error, "Bad file descriptor"); break; -#endif -#ifdef ECHILD case ECHILD: - strcpy(error, "No children"); - break; -#endif -#ifdef EDESTADDRREQ - case EDESTADDRREQ: - strcpy(error, "Destination address required"); + strcpy(error, "No child processes"); break; -#endif -#ifdef EAGAIN case EAGAIN: strcpy(error, "Resource temporarily unavailable"); break; -#endif -#ifdef ENOMEM case ENOMEM: - strcpy(error, "Not enough space"); + strcpy(error, "Not enough memory"); break; -#endif -#ifdef EACCES case EACCES: strcpy(error, "Permission denied"); break; -#endif -#ifdef EFAULT case EFAULT: strcpy(error, "Bad address"); break; -#endif -#ifdef ENOTBLK case ENOTBLK: strcpy(error, "Block device required"); break; -#endif -#ifdef EBUSY case EBUSY: strcpy(error, "Device or resource busy"); break; -#endif -#ifdef EEXIST case EEXIST: strcpy(error, "File exists"); break; -#endif -#ifdef EXDEV case EXDEV: strcpy(error, "Cross-device link"); break; -#endif -#ifdef ENODEV case ENODEV: strcpy(error, "No such device"); break; -#endif -#ifdef ENOTDIR case ENOTDIR: strcpy(error, "Not a directory"); break; -#endif -#ifdef EHOSTDOWN - case EHOSTDOWN: - strcpy(error, "Host is down"); - break; -#endif -#ifdef EINPROGRESS - case EINPROGRESS: - strcpy(error, "Connection already in progress"); - break; -#endif -#ifdef EISDIR case EISDIR: strcpy(error, "Is a directory"); break; -#endif -#ifdef EINVAL case EINVAL: strcpy(error, "Invalid argument"); break; -#endif -#ifdef EISNAM - case EISNAM: - strcpy(error, "Is a named type file"); - break; -#endif -#ifdef ENETDOWN - case ENETDOWN: - strcpy(error, "Network interface is not configured"); - break; -#endif -#ifdef ENFILE case ENFILE: strcpy(error, "Too many open files in system"); break; -#endif -#ifdef EMFILE case EMFILE: strcpy(error, "Too many open files"); break; -#endif -#ifdef ENOTTY case ENOTTY: - strcpy(error, "Not a character device"); + strcpy(error, "Inappropriate I/O control operation"); break; -#endif -#ifdef ETXTBSY case ETXTBSY: strcpy(error, "Text file busy"); break; -#endif -#ifdef EFBIG case EFBIG: strcpy(error, "File too large"); break; -#endif -#ifdef EHOSTUNREACH - case EHOSTUNREACH: - strcpy(error, "Host is unreachable"); - break; -#endif -#ifdef ENOSPC case ENOSPC: strcpy(error, "No space left on device"); break; -#endif -#ifdef ENOTSUP - case ENOTSUP: - strcpy(error, "Not supported"); - break; -#endif -#ifdef ESPIPE case ESPIPE: strcpy(error, "Illegal seek"); break; -#endif -#ifdef EROFS case EROFS: strcpy(error, "Read-only file system"); break; -#endif -#ifdef EMLINK case EMLINK: strcpy(error, "Too many links"); break; -#endif -#ifdef EPIPE case EPIPE: strcpy(error, "Broken pipe"); break; -#endif -#ifdef EDOM case EDOM: - strcpy(error, "Math argument"); + strcpy(error, "Mathematics argument out of domain"); break; -#endif -#ifdef ERANGE case ERANGE: - strcpy(error, "Result too large"); + strcpy(error, "Result out of range"); + break; + case EDEADLK: + strcpy(error, "Resource deadlock would occur"); + break; + case ENAMETOOLONG: + strcpy(error, "File name too long"); + break; + case ENOLCK: + strcpy(error, "No locks available"); + break; + case ENOSYS: + strcpy(error, "Function not implemented"); + break; + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; + case ELOOP: + strcpy(error, "Too many symbolic links encountered"); break; -#endif -#ifdef ENOMSG case ENOMSG: strcpy(error, "No message of desired type"); break; -#endif -#ifdef EIDRM case EIDRM: strcpy(error, "Identifier removed"); break; -#endif -#ifdef EDEADLK - case EDEADLK: - strcpy(error, "Deadlock"); + case ECHRNG: + strcpy(error, "Channel number out of range"); break; -#endif -#ifdef ENETUNREACH - case ENETUNREACH: - strcpy(error, "Network is unreachable"); + case EL2NSYNC: + strcpy(error, "Level 2 not synchronized"); break; -#endif -#ifdef ENOLCK - case ENOLCK: - strcpy(error, "No lock"); + case EL3HLT: + strcpy(error, "Level 3 halted"); + break; + case EL3RST: + strcpy(error, "Level 3 reset"); + break; + case ELNRNG: + strcpy(error, "Link number out of range"); + break; + case EUNATCH: + strcpy(error, "Protocol driver not attached"); + break; + case ENOCSI: + strcpy(error, "No CSI structure available"); + break; + case EL2HLT: + strcpy(error, "Level 2 halted"); + break; + case EBADE: + strcpy(error, "Invalid exchange"); + break; + case EBADR: + strcpy(error, "Invalid request descriptor"); + break; + case EXFULL: + strcpy(error, "Exchange full"); + break; + case ENOANO: + strcpy(error, "No anode"); + break; + case EBADRQC: + strcpy(error, "Invalid request code"); + break; + case EBADSLT: + strcpy(error, "Invalid slot"); break; -#endif -#ifdef ENOSTR case ENOSTR: - strcpy(error, "Not a stream"); + strcpy(error, "Device not a stream"); + break; + case ENODATA: + strcpy(error, "No data available"); break; -#endif -#ifdef ETIME case ETIME: - strcpy(error, "Stream ioctl timeout"); + strcpy(error, "Timer expired"); break; -#endif -#ifdef ENOSR case ENOSR: - strcpy(error, "No stream resources"); + strcpy(error, "Out of stream resources"); break; -#endif -#ifdef ENONET case ENONET: - strcpy(error, "Machine is not on the network"); + strcpy(error, "Machine not on network"); break; -#endif -#ifdef ENOPKG case ENOPKG: - strcpy(error, "No package"); + strcpy(error, "Package not installed"); break; -#endif -#ifdef EREMOTE case EREMOTE: - strcpy(error, "Resource is remote"); + strcpy(error, "Object is remote"); break; -#endif -#ifdef ENOLINK case ENOLINK: - strcpy(error, "Virtual circuit is gone"); + strcpy(error, "Link severed"); break; -#endif -#ifdef EADV case EADV: strcpy(error, "Advertise error"); break; -#endif -#ifdef ESRMNT case ESRMNT: - strcpy(error, "Srmount error"); + strcpy(error, "SR mount error"); break; -#endif -#ifdef ECOMM case ECOMM: - strcpy(error, "Communication error"); + strcpy(error, "Communication error on send"); break; -#endif -#ifdef EPROTO case EPROTO: strcpy(error, "Protocol error"); break; -#endif -#ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: - strcpy(error, "Unknown protocol"); - break; -#endif -#ifdef EMULTIHOP case EMULTIHOP: strcpy(error, "Multihop attempted"); break; -#endif -#ifdef EBADMSG case EBADMSG: - strcpy(error, "Bad message"); + strcpy(error, "Not a data message"); + break; + case EOVERFLOW: + strcpy(error, "Value too large for data type"); + break; + case ENOTUNIQ: + strcpy(error, "Name not unique on network"); + break; + case EBADFD: + strcpy(error, "File descriptor in bad state"); + break; + case EREMCHG: + strcpy(error, "Remote address changed"); break; -#endif -#ifdef ELIBACC case ELIBACC: strcpy(error, "Cannot access a needed shared library"); break; -#endif -#ifdef ELIBBAD case ELIBBAD: strcpy(error, "Accessing a corrupted shared library"); break; -#endif -#ifdef ELIBSCN case ELIBSCN: - strcpy(error, ".lib section in a.out corrupted"); + strcpy(error, "Corrupted .lib section in a.out"); break; -#endif -#ifdef ELIBMAX case ELIBMAX: - strcpy(error, - "Attempting to link in more shared libraries than system limit"); + strcpy(error, "Exceeded shared library system limit"); break; -#endif -#ifdef ELIBEXEC case ELIBEXEC: - strcpy(error, "Cannot exec a shared library directly"); + strcpy(error, "Cannot execute shared library directly"); break; -#endif -#ifdef ENOSYS - case ENOSYS: - strcpy(error, "Function not implemented"); + case EUCLEAN: + strcpy(error, "Structure needs cleaning"); break; -#endif -#ifdef ENMFILE - case ENMFILE: - strcpy(error, "No more files"); + case ENOTNAM: + strcpy(error, "Not a XENIX named type file"); break; -#endif -#ifdef ENOTEMPTY - case ENOTEMPTY: - strcpy(error, "Directory not empty"); + case ENAVAIL: + strcpy(error, "No XENIX semaphores available"); break; -#endif -#ifdef ENAMETOOLONG - case ENAMETOOLONG: - strcpy(error, "File or path name too long"); + case EISNAM: + strcpy(error, "Is a named type file"); + break; + case EREMOTEIO: + strcpy(error, "Remote I/O error"); + break; + case EDQUOT: + strcpy(error, "Quota exceeded"); + break; + case ENOMEDIUM: + strcpy(error, "No medium found"); + break; + case EMEDIUMTYPE: + strcpy(error, "Wrong medium type"); break; -#endif -#ifdef ELOOP - case ELOOP: - strcpy(error, "Too many symbolic links"); - break; -#endif -#ifdef ENOBUFS - case ENOBUFS: - strcpy(error, "No buffer space available"); - break; -#endif -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - strcpy(error, "Address family not supported by protocol family"); - break; -#endif -#ifdef EPROTOTYPE - case EPROTOTYPE: - strcpy(error, "Protocol wrong type for socket"); - break; -#endif -#ifdef ENOTSOCK - case ENOTSOCK: - strcpy(error, "Socket operation on non-socket"); - break; -#endif -#ifdef ENOPROTOOPT - case ENOPROTOOPT: - strcpy(error, "Protocol not available"); - break; -#endif -#ifdef ESHUTDOWN - case ESHUTDOWN: - strcpy(error, "Can't send after socket shutdown"); - break; -#endif -#ifdef ECONNREFUSED - case ECONNREFUSED: - strcpy(error, "Connection refused"); - break; -#endif -#ifdef EADDRINUSE - case EADDRINUSE: - strcpy(error, "Address already in use"); - break; -#endif -#ifdef ECONNABORTED - case ECONNABORTED: - strcpy(error, "Software caused connection abort"); - break; -#endif -#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) - case EWOULDBLOCK: - strcpy(error, "Operation would block"); - break; -#endif -#ifdef ENOTCONN - case ENOTCONN: - strcpy(error, "Socket is not connected"); - break; -#endif -#ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - strcpy(error, "Socket type not supported"); - break; -#endif -#ifdef EISCONN - case EISCONN: - strcpy(error, "Socket is already connected"); - break; -#endif -#ifdef ECANCELED - case ECANCELED: - strcpy(error, "Operation canceled"); - break; -#endif -#ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: - strcpy(error, "State not recoverable"); - break; -#endif -#ifdef EOWNERDEAD - case EOWNERDEAD: - strcpy(error, "Previous owner died"); - break; -#endif -#ifdef ESTRPIPE - case ESTRPIPE: - strcpy(error, "Streams pipe error"); - break; -#endif -#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) - case EOPNOTSUPP: - strcpy(error, "Operation not supported on socket"); - break; -#endif -#ifdef EMSGSIZE - case EMSGSIZE: - strcpy(error, "Message too long"); - break; -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: - strcpy(error, "Connection timed out"); - break; -#endif -#ifdef ENOTSCHEDULABLE case ENOTSCHEDULABLE: - strcpy(error, "The process cannot be scheduled"); + strcpy(error, "Process cannot be scheduled"); break; -#endif default: strcpy(error, "Unknown error"); break; diff --git a/libc/src/sys/errno.c b/libc/src/sys/errno.c index 0f49578b..7ec82265 100644 --- a/libc/src/sys/errno.c +++ b/libc/src/sys/errno.c @@ -3,7 +3,7 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/errno.h" +#include "errno.h" /// @brief Returns the error number for the current process. /// @return Pointer to the error number. diff --git a/libc/src/sys/ioctl.c b/libc/src/sys/ioctl.c index b8b81ba2..f2d865cc 100644 --- a/libc/src/sys/ioctl.c +++ b/libc/src/sys/ioctl.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "sys/ioctl.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(int, ioctl, int, fd, unsigned long int, request, void *, data) +// _syscall3(long, ioctl, int, fd, unsigned int, request, unsigned long, data) +long ioctl(int fd, unsigned int request, unsigned long data) +{ + long __res; + __inline_syscall_3(__res, ioctl, fd, request, data); + __syscall_return(long, __res); +} diff --git a/libc/src/sys/ipc.c b/libc/src/sys/ipc.c index 58c04d46..14e97c5a 100644 --- a/libc/src/sys/ipc.c +++ b/libc/src/sys/ipc.c @@ -8,32 +8,80 @@ #include "sys/shm.h" #include "sys/sem.h" #include "sys/msg.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/stat.h" -#include "io/debug.h" #include "system/syscall_types.h" +#include "stdio.h" -_syscall3(void *, shmat, int, shmid, const void *, shmaddr, int, shmflg) +// _syscall3(void *, shmat, int, shmid, const void *, shmaddr, int, shmflg) +void *shmat(int shmid, const void *shmaddr, int shmflg) +{ + long __res; + __inline_syscall_3(__res, shmat, shmid, shmaddr, shmflg); + __syscall_return(void *, __res); +} -_syscall3(long, shmget, key_t, key, size_t, size, int, flag) +// _syscall3(long, shmget, key_t, key, size_t, size, int, flag) +long shmget(key_t key, size_t size, int flag) +{ + long __res; + __inline_syscall_3(__res, shmget, key, size, flag); + __syscall_return(long, __res); +} -_syscall1(long, shmdt, const void *, shmaddr) +// _syscall1(long, shmdt, const void *, shmaddr) +long shmdt(const void *shmaddr) +{ + long __res; + __inline_syscall_1(__res, shmdt, shmaddr); + __syscall_return(long, __res); +} -_syscall3(long, shmctl, int, shmid, int, cmd, struct shmid_ds *, buf) +// _syscall3(long, shmctl, int, shmid, int, cmd, struct shmid_ds *, buf) +long shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + long __res; + __inline_syscall_3(__res, shmctl, shmid, cmd, buf); + __syscall_return(long, __res); +} -_syscall3(long, semget, key_t, key, int, nsems, int, semflg) +// _syscall3(long, semget, key_t, key, int, nsems, int, semflg) +long semget(key_t key, int nsems, int semflg) +{ + long __res; + __inline_syscall_3(__res, semget, key, nsems, semflg); + __syscall_return(long, __res); +} -_syscall4(long, semctl, int, semid, int, semnum, int, cmd, union semun *, arg) +// _syscall4(long, semctl, int, semid, int, semnum, int, cmd, union semun *, arg) +long semctl(int semid, int semnum, int cmd, union semun *arg) +{ + long __res; + __inline_syscall_4(__res, semctl, semid, semnum, cmd, arg); + __syscall_return(long, __res); +} -_syscall2(int, msgget, key_t, key, int, msgflg) +// _syscall2(int, msgget, key_t, key, int, msgflg) +int msgget(key_t key, int msgflg) +{ + long __res; + __inline_syscall_2(__res, msgget, key, msgflg); + __syscall_return(int, __res); +} -_syscall3(int, msgctl, int, msqid, int, cmd, struct msqid_ds *, buf) +// _syscall3(int, msgctl, int, msqid, int, cmd, struct msqid_ds *, buf) +int msgctl(int msqid, int cmd, struct msqid_ds *buf) +{ + long __res; + __inline_syscall_3(__res, msgctl, msqid, cmd, buf); + __syscall_return(int, __res); +} int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) { long __res; do { - __inline_syscall4(__res, msgsnd, msqid, msgp, msgsz, msgflg); + __inline_syscall_4(__res, msgsnd, msqid, msgp, msgsz, msgflg); if (!(msgflg & IPC_NOWAIT) && (__res == -EAGAIN)) { continue; } @@ -46,7 +94,7 @@ ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) { long __res; do { - __inline_syscall5(__res, msgrcv, msqid, msgp, msgsz, msgtyp, msgflg); + __inline_syscall_5(__res, msgrcv, msqid, msgp, msgsz, msgtyp, msgflg); if (!(msgflg & IPC_NOWAIT) && ((__res == -EAGAIN) || (__res == -ENOMSG))) { continue; } @@ -62,14 +110,14 @@ long semop(int semid, struct sembuf *sops, unsigned nsops) // The pointer to the operation is NULL. if (!sops) { - pr_err("The pointer to the operation is NULL.\n"); + perror("The pointer to the operation is NULL.\n"); errno = EINVAL; return -1; } // The value of nsops is negative. if (nsops <= 0) { - pr_err("The value of nsops is negative.\n"); + perror("The value of nsops is negative.\n"); errno = EINVAL; return -1; } @@ -82,7 +130,7 @@ long semop(int semid, struct sembuf *sops, unsigned nsops) // or receives an error. while (1) { // Calling the kernel-side function. - __inline_syscall3(__res, semop, semid, op, 1); + __inline_syscall_3(__res, semop, semid, op, 1); // If we get an error, the operation has been taken care of we stop // the loop. We also stop the loop if the operation is not allowed @@ -103,13 +151,13 @@ long semop(int semid, struct sembuf *sops, unsigned nsops) __syscall_return(long, __res); } -key_t ftok(char *path, int id) +key_t ftok(const char *path, int id) { // Create a struct containing the serial number and the device number of the // file we use to generate the key. struct stat st; if (stat(path, &st) < 0) { - pr_err("Cannot stat the file `%s`...\n", path); + printf("Cannot stat the file `%s`.\n"); errno = ENOENT; return -1; } diff --git a/libc/src/sys/mman.c b/libc/src/sys/mman.c index c4820625..11dbec4d 100644 --- a/libc/src/sys/mman.c +++ b/libc/src/sys/mman.c @@ -4,10 +4,26 @@ /// See LICENSE.md for details. #include "sys/mman.h" -#include "sys/errno.h" +#include "errno.h" #include "unistd.h" #include "system/syscall_types.h" -_syscall6(void *, mmap, void *, addr, size_t, length, int, prot, int, flags, int, fd, off_t, offset) +#if 0 -_syscall2(int, munmap, void *, addr, size_t, length) +// _syscall6(void *, mmap, void *, addr, size_t, length, int, prot, int, flags, int, fd, off_t, offset) +void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + long __res; + __inline_syscall_6(__res, mmap, addr, length, prot, flags, fd, offset); + __syscall_return(void *, __res); +} + +// _syscall2(int, munmap, void *, addr, size_t, length) +int munmap(void *addr, size_t length) +{ + long __res; + __inline_syscall_2(__res, munmap, addr, length); + __syscall_return(int, __res); +} + +#endif diff --git a/libc/src/sys/utsname.c b/libc/src/sys/utsname.c index 360c6573..ca041278 100644 --- a/libc/src/sys/utsname.c +++ b/libc/src/sys/utsname.c @@ -5,7 +5,14 @@ #include "sys/utsname.h" #include "stddef.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, uname, utsname_t *, buf) +// _syscall1(int, uname, utsname_t *, buf) + +int uname(utsname_t *buf) +{ + long __res; + __inline_syscall_1(__res, uname, buf); + __syscall_return(int, __res); +} diff --git a/libc/src/syslog.c b/libc/src/syslog.c new file mode 100644 index 00000000..2f944fdb --- /dev/null +++ b/libc/src/syslog.c @@ -0,0 +1,97 @@ +/// @file debug.c +/// @brief Debugging primitives. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "syslog.h" + +#include "system/syscall_types.h" +#include "unistd.h" +#include "stddef.h" +#include "errno.h" +#include "stdio.h" + +// #include "io/ansi_colors.h" +// #include "io/port_io.h" +// #include "math.h" +// #include "string.h" +// #include "sys/bitops.h" + +// Global variables for syslog configuration +static int syslog_facility = LOG_USER; +static int syslog_options = 0; +static const char *syslog_ident = NULL; +static int log_mask = 0xFF; // Mask allowing all levels by default + +void openlog(const char *ident, int option, int facility) +{ + syslog_ident = ident; + syslog_options = option; + syslog_facility = facility; +} + +int setlogmask(int mask) +{ + int old_mask = log_mask; + log_mask = mask; + return old_mask; +} + +void closelog(void) +{ + syslog_ident = NULL; + syslog_options = 0; + syslog_facility = LOG_USER; + log_mask = 0xFF; // Reset mask to allow all levels +} + +int __syslog(const char *file, const char *fun, int line, short log_level, const char *format, ...) +{ + // Check if the message's priority is allowed by the log mask. + if (!(log_mask & (1 << log_level))) { + return 0; + } + + // Buffer to hold the formatted message. + char buf[BUFSIZ]; + int offset = 0; + + // Add identifier if specified. + if (syslog_ident) { + offset += snprintf(buf + offset, sizeof(buf) - offset, "%s: ", syslog_ident); + } + + // Add PID if LOG_PID option is set. + if (syslog_options & LOG_PID) { + offset += snprintf(buf + offset, sizeof(buf) - offset, "[%d] ", getpid()); + } + + // Format the main log message with the variable arguments. + va_list args; + va_start(args, format); + int len = vsnprintf(buf + offset, sizeof(buf) - offset, format, args); + va_end(args); + + // Adjust length if the message was truncated + if (len >= (int)(sizeof(buf) - offset)) { + len = sizeof(buf) - 1; + buf[len] = '\0'; // Null-terminate if truncated + } else { + len += offset; // Include the offset as part of the total length. + } + + // Call the syslog system call to send the formatted message to the system log. + // __inline_syscall_5(len, syslog, type, file, func, line, buf); + __asm__ __volatile__("push %%ebx; movl %2,%%ebx; movl %1,%%eax; " + "int $0x80; pop %%ebx" + : "=a"(len) + : "i"(__NR_syslog), "ri"(file), "c"(fun), "d"(line), "S"(log_level), "D"(buf) + : "memory"); + + // If the syslog system call fails and LOG_CONS is set, write to console as a fallback. + if ((len == -1) && (syslog_options & LOG_CONS)) { + fprintf(stderr, buf, len); + } + + __syscall_return(int, len); +} diff --git a/libc/src/termios.c b/libc/src/termios.c index 97f667c7..568958f2 100644 --- a/libc/src/termios.c +++ b/libc/src/termios.c @@ -5,7 +5,7 @@ #include "termios.h" #include "bits/ioctls.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" int tcgetattr(int fd, termios_t *termios_p) diff --git a/libc/src/time.c b/libc/src/time.c index 28e6c4c0..946508ad 100644 --- a/libc/src/time.c +++ b/libc/src/time.c @@ -6,7 +6,7 @@ #include "time.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" /// @brief List of week days name. @@ -29,10 +29,16 @@ static const char *months_short[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -/// @brief Time function. -_syscall1(time_t, time, time_t *, t) +// _syscall1(time_t, time, time_t *, t) - time_t difftime(time_t time1, time_t time2) +time_t time(time_t *t) +{ + long __res; + __inline_syscall_1(__res, time, t); + __syscall_return(time_t, __res); +} + +time_t difftime(time_t time1, time_t time2) { return time1 - time2; } @@ -427,10 +433,15 @@ size_t strftime(char *str, size_t maxsize, const char *format, const tm_t *timep return ret; } -/// @brief nanosleep function. -_syscall2(int, nanosleep, const struct timespec *, req, struct timespec *, rem) +// _syscall2(int, nanosleep, const struct timespec *, req, struct timespec *, rem) +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + long __res; + __inline_syscall_2(__res, nanosleep, req, rem); + __syscall_return(int, __res); +} - unsigned int sleep(unsigned int seconds) +unsigned int sleep(unsigned int seconds) { struct timespec req, rem; req.tv_sec = seconds; @@ -444,6 +455,18 @@ _syscall2(int, nanosleep, const struct timespec *, req, struct timespec *, rem) return 0; } -_syscall2(int, getitimer, int, which, struct itimerval *, curr_value) +// _syscall2(int, getitimer, int, which, struct itimerval *, curr_value) +int getitimer(int which, struct itimerval *curr_value) +{ + long __res; + __inline_syscall_2(__res, getitimer, which, curr_value); + __syscall_return(int, __res); +} - _syscall3(int, setitimer, int, which, const struct itimerval *, new_value, struct itimerval *, old_value) +// _syscall3(int, setitimer, int, which, const struct itimerval *, new_value, struct itimerval *, old_value) +int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) +{ + long __res; + __inline_syscall_3(__res, setitimer, which, new_value, old_value); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/chdir.c b/libc/src/unistd/chdir.c index 7136a00f..764729cf 100644 --- a/libc/src/unistd/chdir.c +++ b/libc/src/unistd/chdir.c @@ -4,9 +4,21 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, chdir, const char *, path) +// _syscall1(int, chdir, const char *, path) +int chdir(const char *path) +{ + long __res; + __inline_syscall_1(__res, chdir, path); + __syscall_return(int, __res); +} -_syscall1(int, fchdir, int, fd) +// _syscall1(int, fchdir, int, fd) +int fchdir(int fd) +{ + long __res; + __inline_syscall_1(__res, fchdir, fd); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/chmod.c b/libc/src/unistd/chmod.c index f5b3b718..54e28c03 100644 --- a/libc/src/unistd/chmod.c +++ b/libc/src/unistd/chmod.c @@ -4,9 +4,21 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, chmod, const char *, pathname, mode_t, mode) +// _syscall2(int, chmod, const char *, pathname, mode_t, mode) +int chmod(const char *pathname, mode_t mode) +{ + long __res; + __inline_syscall_2(__res, chmod, pathname, mode); + __syscall_return(int, __res); +} -_syscall2(int, fchmod, int, fd, mode_t, mode) +// _syscall2(int, fchmod, int, fd, mode_t, mode) +int fchmod(int fd, mode_t mode) +{ + long __res; + __inline_syscall_2(__res, fchmod, fd, mode); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/chown.c b/libc/src/unistd/chown.c index 50cc8aab..daa1cb26 100644 --- a/libc/src/unistd/chown.c +++ b/libc/src/unistd/chown.c @@ -4,11 +4,29 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(int, chown, const char *, pathname, uid_t, owner, gid_t, group) +// _syscall3(int, chown, const char *, pathname, uid_t, owner, gid_t, group) +int chown(const char *pathname, uid_t owner, gid_t group) +{ + long __res; + __inline_syscall_3(__res, chown, pathname, owner, group); + __syscall_return(int, __res); +} -_syscall3(int, lchown, const char *, pathname, uid_t, owner, gid_t, group) +// _syscall3(int, lchown, const char *, pathname, uid_t, owner, gid_t, group) +int lchown(const char *pathname, uid_t owner, gid_t group) +{ + long __res; + __inline_syscall_3(__res, lchown, pathname, owner, group); + __syscall_return(int, __res); +} -_syscall3(int, fchown, int, fd, uid_t, owner, gid_t, group) +// _syscall3(int, fchown, int, fd, uid_t, owner, gid_t, group) +int fchown(int fd, uid_t owner, gid_t group) +{ + long __res; + __inline_syscall_3(__res, fchown, fd, owner, group); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/close.c b/libc/src/unistd/close.c index 889004a5..d4c4e26a 100644 --- a/libc/src/unistd/close.c +++ b/libc/src/unistd/close.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, close, int, fd) +// _syscall1(int, close, int, fd) +int close(int fd) +{ + long __res; + __inline_syscall_1(__res, close, fd); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/creat.c b/libc/src/unistd/creat.c index 56dc964c..c0625bab 100644 --- a/libc/src/unistd/creat.c +++ b/libc/src/unistd/creat.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, creat, const char *, pathname, mode_t, mode) +// _syscall2(int, creat, const char *, pathname, mode_t, mode) +int creat(const char *pathname, mode_t mode) +{ + long __res; + __inline_syscall_2(__res, creat, pathname, mode); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/dup.c b/libc/src/unistd/dup.c index cdd1736b..9274bdec 100644 --- a/libc/src/unistd/dup.c +++ b/libc/src/unistd/dup.c @@ -5,6 +5,12 @@ #include "unistd.h" #include "system/syscall_types.h" -#include "sys/errno.h" +#include "errno.h" -_syscall1(int, dup, int, fd) +// _syscall1(int, dup, int, fd) +int dup(int fd) +{ + long __res; + __inline_syscall_1(__res, dup, fd); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/exec.c b/libc/src/unistd/exec.c index 80718e19..62bbab48 100644 --- a/libc/src/unistd/exec.c +++ b/libc/src/unistd/exec.c @@ -5,11 +5,10 @@ #include "unistd.h" #include "fcntl.h" -#include "io/debug.h" #include "stdarg.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/stat.h" #include "system/syscall_types.h" #include "limits.h" @@ -62,7 +61,7 @@ static inline int __find_in_path(const char *file, char *buf, size_t buf_len) int execve(const char *path, char *const argv[], char *const envp[]) { long __res; - __inline_syscall3(__res, execve, path, argv, envp); + __inline_syscall_3(__res, execve, path, argv, envp); __syscall_return(int, __res); } diff --git a/libc/src/unistd/exit.c b/libc/src/unistd/exit.c index 29b37e61..cbafb124 100644 --- a/libc/src/unistd/exit.c +++ b/libc/src/unistd/exit.c @@ -9,6 +9,6 @@ void exit(int status) { long __res; - __inline_syscall1(__res, exit, status); + __inline_syscall_1(__res, exit, status); // The process never returns from this system call! } diff --git a/libc/src/unistd/fcntl.c b/libc/src/unistd/fcntl.c new file mode 100644 index 00000000..0971eef4 --- /dev/null +++ b/libc/src/unistd/fcntl.c @@ -0,0 +1,16 @@ +/// @file fcntl.c +/// @brief Input/Output ConTroL (IOCTL) functions implementation. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "system/syscall_types.h" +#include "fcntl.h" +#include "errno.h" + +// _syscall3(long, fcntl, int, fd, unsigned int, request, unsigned long, data) +long fcntl(int fd, unsigned int request, unsigned long data) +{ + long __res; + __inline_syscall_3(__res, fcntl, fd, request, data); + __syscall_return(long, __res); +} diff --git a/libc/src/unistd/fork.c b/libc/src/unistd/fork.c index 9d83cb1d..c6c023d5 100644 --- a/libc/src/unistd/fork.c +++ b/libc/src/unistd/fork.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(pid_t, fork) +// _syscall0(pid_t, fork) +pid_t fork(void) +{ + long __res; + __inline_syscall_0(__res, fork); + __syscall_return(pid_t, __res); +} diff --git a/libc/src/unistd/getcwd.c b/libc/src/unistd/getcwd.c index b33a2839..238d46fd 100644 --- a/libc/src/unistd/getcwd.c +++ b/libc/src/unistd/getcwd.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(char *, getcwd, char *, buf, size_t, size) +// _syscall2(char *, getcwd, char *, buf, size_t, size) +char *getcwd(char *buf, size_t size) +{ + long __res; + __inline_syscall_2(__res, getcwd, buf, size); + __syscall_return(char *, __res); +} diff --git a/libc/src/unistd/getdents.c b/libc/src/unistd/getdents.c index 6abe6ebe..df192c03 100644 --- a/libc/src/unistd/getdents.c +++ b/libc/src/unistd/getdents.c @@ -4,9 +4,14 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" #include "dirent.h" - -_syscall3(ssize_t, getdents, int, fd, dirent_t *, dirp, unsigned int, count) +// _syscall3(ssize_t, getdents, int, fd, dirent_t *, dirp, unsigned int, count) +ssize_t getdents(int fd, dirent_t *dirp, unsigned int count) +{ + long __res; + __inline_syscall_3(__res, getdents, fd, dirp, count); + __syscall_return(ssize_t, __res); +} diff --git a/libc/src/unistd/getgid.c b/libc/src/unistd/getgid.c index beab241c..569322b4 100644 --- a/libc/src/unistd/getgid.c +++ b/libc/src/unistd/getgid.c @@ -4,8 +4,21 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(pid_t, getgid) -_syscall0(gid_t, getegid) +// _syscall0(pid_t, getgid) +pid_t getgid(void) +{ + long __res; + __inline_syscall_0(__res, getgid); + __syscall_return(pid_t, __res); +} + +// _syscall0(gid_t, getegid) +gid_t getegid(void) +{ + long __res; + __inline_syscall_0(__res, getegid); + __syscall_return(gid_t, __res); +} diff --git a/libc/src/unistd/getpgid.c b/libc/src/unistd/getpgid.c index 947549bb..eba19e9c 100644 --- a/libc/src/unistd/getpgid.c +++ b/libc/src/unistd/getpgid.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(pid_t, getpgid, pid_t, pid) +// _syscall1(pid_t, getpgid, pid_t, pid) +pid_t getpgid(pid_t pid) +{ + long __res; + __inline_syscall_1(__res, getpgid, pid); + __syscall_return(pid_t, __res); +} diff --git a/libc/src/unistd/getpid.c b/libc/src/unistd/getpid.c index 301b02fa..596b8ce0 100644 --- a/libc/src/unistd/getpid.c +++ b/libc/src/unistd/getpid.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(pid_t, getpid) +// _syscall0(pid_t, getpid) +pid_t getpid(void) +{ + long __res; + __inline_syscall_0(__res, getpid); + __syscall_return(pid_t, __res); +} diff --git a/libc/src/unistd/getppid.c b/libc/src/unistd/getppid.c index fc4246c3..d9192838 100644 --- a/libc/src/unistd/getppid.c +++ b/libc/src/unistd/getppid.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(pid_t, getppid) +// _syscall0(pid_t, getppid) +pid_t getppid(void) +{ + long __res; + __inline_syscall_0(__res, getppid); + __syscall_return(pid_t, __res); +} diff --git a/libc/src/unistd/getsid.c b/libc/src/unistd/getsid.c index 34590b24..f0ff0149 100644 --- a/libc/src/unistd/getsid.c +++ b/libc/src/unistd/getsid.c @@ -4,7 +4,14 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(pid_t, getsid, pid_t, pid) +// _syscall1(pid_t, getsid, pid_t, pid) +pid_t getsid(pid_t pid) +{ + long __res; + __inline_syscall_1(__res, getsid, pid); + __syscall_return(pid_t, __res); +} + diff --git a/libc/src/unistd/getuid.c b/libc/src/unistd/getuid.c index d2db3e47..b8d1eb0d 100644 --- a/libc/src/unistd/getuid.c +++ b/libc/src/unistd/getuid.c @@ -4,8 +4,21 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(uid_t, getuid) -_syscall0(uid_t, geteuid) +// _syscall0(uid_t, getuid) +uid_t getuid(void) +{ + long __res; + __inline_syscall_0(__res, getuid); + __syscall_return(uid_t, __res); +} + +// _syscall0(uid_t, geteuid) +uid_t geteuid(void) +{ + long __res; + __inline_syscall_0(__res, geteuid); + __syscall_return(uid_t, __res); +} diff --git a/libc/src/unistd/interval.c b/libc/src/unistd/interval.c index 37af77bb..1c943346 100644 --- a/libc/src/unistd/interval.c +++ b/libc/src/unistd/interval.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(unsigned, alarm, int, seconds) +// _syscall1(unsigned, alarm, int, seconds) +unsigned alarm(int seconds) +{ + long __res; + __inline_syscall_1(__res, alarm, seconds); + __syscall_return(unsigned, __res); +} diff --git a/libc/src/unistd/kill.c b/libc/src/unistd/kill.c index cb72a814..cc85ec1d 100644 --- a/libc/src/unistd/kill.c +++ b/libc/src/unistd/kill.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, kill, pid_t, pid, int, sig) +// _syscall2(int, kill, pid_t, pid, int, sig) +int kill(pid_t pid, int sig) +{ + long __res; + __inline_syscall_2(__res, kill, pid, sig); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/lseek.c b/libc/src/unistd/lseek.c index a4baa472..8cf8d47c 100644 --- a/libc/src/unistd/lseek.c +++ b/libc/src/unistd/lseek.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(off_t, lseek, int, fd, off_t, offset, int, whence) +// _syscall3(off_t, lseek, int, fd, off_t, offset, int, whence) +off_t lseek(int fd, off_t offset, int whence) +{ + long __res; + __inline_syscall_3(__res, lseek, fd, offset, whence); + __syscall_return(off_t, __res); +} diff --git a/libc/src/unistd/mkdir.c b/libc/src/unistd/mkdir.c index 2673d628..3db73889 100644 --- a/libc/src/unistd/mkdir.c +++ b/libc/src/unistd/mkdir.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, mkdir, const char *, path, mode_t, mode) +// _syscall2(int, mkdir, const char *, path, mode_t, mode) +int mkdir(const char *path, mode_t mode) +{ + long __res; + __inline_syscall_2(__res, mkdir, path, mode); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/nice.c b/libc/src/unistd/nice.c index e1ff11bd..4c6bc28f 100644 --- a/libc/src/unistd/nice.c +++ b/libc/src/unistd/nice.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, nice, int, inc) +// _syscall1(int, nice, int, inc) +int nice(int inc) +{ + long __res; + __inline_syscall_1(__res, nice, inc); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/open.c b/libc/src/unistd/open.c index 7719ca2d..8d154cd0 100644 --- a/libc/src/unistd/open.c +++ b/libc/src/unistd/open.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(int, open, const char *, pathname, int, flags, mode_t, mode) +// _syscall3(int, open, const char *, pathname, int, flags, mode_t, mode) +int open(const char *pathname, int flags, mode_t mode) +{ + long __res; + __inline_syscall_3(__res, open, pathname, flags, mode); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/pipe.c b/libc/src/unistd/pipe.c new file mode 100644 index 00000000..7ab08b69 --- /dev/null +++ b/libc/src/unistd/pipe.c @@ -0,0 +1,16 @@ +/// @file pipe.c +/// @brief System call wrapper for creating a pipe +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "unistd.h" +#include "errno.h" +#include "system/syscall_types.h" + +// _syscall1(int, pipe, int *, fds) +int pipe(int fds[2]) +{ + long __res; + __inline_syscall_1(__res, pipe, fds); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/read.c b/libc/src/unistd/read.c index 80224533..70416f62 100644 --- a/libc/src/unistd/read.c +++ b/libc/src/unistd/read.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes) +// _syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes) +ssize_t read(int fd, void *buf, size_t nbytes) +{ + long __res; + __inline_syscall_3(__res, read, fd, buf, nbytes); + __syscall_return(ssize_t, __res); +} diff --git a/libc/src/unistd/readlink.c b/libc/src/unistd/readlink.c index 7637f29b..94fe2a7c 100644 --- a/libc/src/unistd/readlink.c +++ b/libc/src/unistd/readlink.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(int, readlink, const char *, path, char *, buffer, size_t, bufsize) +// _syscall3(int, readlink, const char *, path, char *, buffer, size_t, bufsize) +int readlink(const char *path, char *buffer, size_t bufsize) +{ + long __res; + __inline_syscall_3(__res, readlink, path, buffer, bufsize); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/reboot.c b/libc/src/unistd/reboot.c index 7d73f411..64fd87a6 100644 --- a/libc/src/unistd/reboot.c +++ b/libc/src/unistd/reboot.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg) +// _syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg) +int reboot(int magic1, int magic2, unsigned int cmd, void *arg) +{ + long __res; + __inline_syscall_4(__res, reboot, magic1, magic2, cmd, arg); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/rmdir.c b/libc/src/unistd/rmdir.c index 516c1ef5..d0a58363 100644 --- a/libc/src/unistd/rmdir.c +++ b/libc/src/unistd/rmdir.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, rmdir, const char *, path) +// _syscall1(int, rmdir, const char *, path) +int rmdir(const char *path) +{ + long __res; + __inline_syscall_1(__res, rmdir, path); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/setgid.c b/libc/src/unistd/setgid.c index 38e90b39..1fc01aa1 100644 --- a/libc/src/unistd/setgid.c +++ b/libc/src/unistd/setgid.c @@ -3,9 +3,23 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "unistd.h" -#include "sys/errno.h" #include "system/syscall_types.h" -_syscall1(int, setgid, pid_t, pid) -_syscall2(int, setregid, gid_t, rgid, gid_t, egid) +#include "unistd.h" +#include "errno.h" + +// _syscall1(int, setgid, pid_t, gid) +int setgid(gid_t gid) +{ + long __res; + __inline_syscall_1(__res, setgid, gid); + __syscall_return(int, __res); +} + +// _syscall2(int, setregid, gid_t, rgid, gid_t, egid) +int setregid(gid_t rgid, gid_t egid) +{ + long __res; + __inline_syscall_2(__res, setregid, rgid, egid); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/setpgid.c b/libc/src/unistd/setpgid.c index 3db169d1..9a7bafcf 100644 --- a/libc/src/unistd/setpgid.c +++ b/libc/src/unistd/setpgid.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, setpgid, pid_t, pid, pid_t, pgid) +// _syscall2(int, setpgid, pid_t, pid, pid_t, pgid) +int setpgid(pid_t pid, pid_t pgid) +{ + long __res; + __inline_syscall_2(__res, setpgid, pid, pgid); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/setsid.c b/libc/src/unistd/setsid.c index a40aaa5c..37108c1f 100644 --- a/libc/src/unistd/setsid.c +++ b/libc/src/unistd/setsid.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall0(pid_t, setsid) +// _syscall0(pid_t, setsid) +pid_t setsid(void) +{ + long __res; + __inline_syscall_0(__res, setsid); + __syscall_return(pid_t, __res); +} diff --git a/libc/src/unistd/setuid.c b/libc/src/unistd/setuid.c index 1a742534..f81a6951 100644 --- a/libc/src/unistd/setuid.c +++ b/libc/src/unistd/setuid.c @@ -4,8 +4,21 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, setuid, uid_t, pid) -_syscall2(int, setreuid, uid_t, ruid, uid_t, euid) +// _syscall1(int, setuid, uid_t, pid) +int setuid(uid_t pid) +{ + long __res; + __inline_syscall_1(__res, setuid, pid); + __syscall_return(int, __res); +} + +// _syscall2(int, setreuid, uid_t, ruid, uid_t, euid) +int setreuid(uid_t ruid, uid_t euid) +{ + long __res; + __inline_syscall_2(__res, setreuid, ruid, euid); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/signal.c b/libc/src/unistd/signal.c index e0a89f81..da96885d 100644 --- a/libc/src/unistd/signal.c +++ b/libc/src/unistd/signal.c @@ -4,17 +4,34 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" #include "signal.h" #include "sys/bitops.h" -/// @brief Implement the sigreturn function. -_syscall0(int, sigreturn) +// _syscall0(int, sigreturn) -/// @brief Implement the sigprocmask function. -_syscall3(int, sigprocmask, int, how, const sigset_t *, set, sigset_t *, oldset) +/// @brief Handles the return from a signal handler. +/// @details +/// This function is used to transition control back to the point where a signal +/// handler was invoked. It performs the necessary system call to complete the +/// signal return process. +/// @return int The result of the signal return system call. +int sigreturn(void) +{ + long __res; + __inline_syscall_0(__res, sigreturn); + __syscall_return(int, __res); +} + +// _syscall3(int, sigprocmask, int, how, const sigset_t *, set, sigset_t *, oldset) +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ + long __res; + __inline_syscall_3(__res, sigprocmask, how, set, oldset); + __syscall_return(int, __res); +} /// @brief List of signals names. static const char *sys_siglist[] = { @@ -55,14 +72,14 @@ static const char *sys_siglist[] = { sighandler_t signal(int signum, sighandler_t handler) { long __res; - __inline_syscall3(__res, signal, signum, handler, (unsigned int)sigreturn); + __inline_syscall_3(__res, signal, signum, handler, (unsigned int)sigreturn); __syscall_return(sighandler_t, __res); } int sigaction(int signum, const sigaction_t *act, sigaction_t *oldact) { long __res; - __inline_syscall4(__res, sigaction, signum, act, oldact, (unsigned int)sigreturn); + __inline_syscall_4(__res, sigaction, signum, act, oldact, (unsigned int)sigreturn); __syscall_return(int, __res); } @@ -70,7 +87,7 @@ const char *strsignal(int sig) { if ((sig >= SIGHUP) && (sig < NSIG)) { return sys_siglist[sig - 1]; -} + } return NULL; } @@ -114,6 +131,6 @@ int sigismember(sigset_t *set, int signum) { if (set) { return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32); -} + } return -1; } diff --git a/libc/src/unistd/stat.c b/libc/src/unistd/stat.c index 49897b14..043b5905 100644 --- a/libc/src/unistd/stat.c +++ b/libc/src/unistd/stat.c @@ -4,11 +4,23 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" #include "sys/stat.h" -_syscall2(int, stat, const char *, path, stat_t *, buf) +// _syscall2(int, stat, const char *, path, stat_t *, buf) +int stat(const char *path, stat_t *buf) +{ + long __res; + __inline_syscall_2(__res, stat, path, buf); + __syscall_return(int, __res); +} -_syscall2(int, fstat, int, fd, stat_t *, buf) +// _syscall2(int, fstat, int, fd, stat_t *, buf) +int fstat(int fd, stat_t *buf) +{ + long __res; + __inline_syscall_2(__res, fstat, fd, buf); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/symlink.c b/libc/src/unistd/symlink.c index 6009de84..0f15890c 100644 --- a/libc/src/unistd/symlink.c +++ b/libc/src/unistd/symlink.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall2(int, symlink, const char *, linkname, const char *, path) +// _syscall2(int, symlink, const char *, linkname, const char *, path) +int symlink(const char *linkname, const char *path) +{ + long __res; + __inline_syscall_2(__res, symlink, linkname, path); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/unlink.c b/libc/src/unistd/unlink.c index 9ae68b7b..8b880c07 100644 --- a/libc/src/unistd/unlink.c +++ b/libc/src/unistd/unlink.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall1(int, unlink, const char *, path) +// _syscall1(int, unlink, const char *, path) +int unlink(const char *path) +{ + long __res; + __inline_syscall_1(__res, unlink, path); + __syscall_return(int, __res); +} diff --git a/libc/src/unistd/waitpid.c b/libc/src/unistd/waitpid.c index 773cd1a6..b3ccfe07 100644 --- a/libc/src/unistd/waitpid.c +++ b/libc/src/unistd/waitpid.c @@ -4,11 +4,11 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/wait.h" #include "system/syscall_types.h" -#include "sys/errno.h" +#include "errno.h" #include "unistd.h" #include "system/syscall_types.h" @@ -17,7 +17,7 @@ pid_t waitpid(pid_t pid, int *status, int options) pid_t __res; int __status = 0; do { - __inline_syscall3(__res, waitpid, pid, &__status, options); + __inline_syscall_3(__res, waitpid, pid, &__status, options); if (__res != 0) { break; } diff --git a/libc/src/unistd/write.c b/libc/src/unistd/write.c index 51225296..3d8e5622 100644 --- a/libc/src/unistd/write.c +++ b/libc/src/unistd/write.c @@ -4,7 +4,13 @@ /// See LICENSE.md for details. #include "unistd.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall_types.h" -_syscall3(ssize_t, write, int, fd, const void *, buf, size_t, nbytes) +// _syscall3(ssize_t, write, int, fd, const void *, buf, size_t, nbytes) +ssize_t write(int fd, const void *buf, size_t nbytes) +{ + long __res; + __inline_syscall_3(__res, write, fd, buf, nbytes); + __syscall_return(ssize_t, __res); +} diff --git a/libc/src/vsprintf.c b/libc/src/vsprintf.c index 83e88189..5d1bf623 100644 --- a/libc/src/vsprintf.c +++ b/libc/src/vsprintf.c @@ -3,12 +3,6 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[PRINTF]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include "sys/bitops.h" #include "ctype.h" #include "fcvt.h" @@ -45,7 +39,6 @@ static inline int skip_atoi(const char **s) { // Error check to ensure that the string pointer is valid. if (s == NULL || *s == NULL) { - pr_crit("skip_atoi: Invalid string pointer.\n"); return -1; } @@ -86,7 +79,6 @@ static char *number(char *str, char *end, long num, int base, int size, int32_t // Error handling: base must be between 2 and 36. if (base < 2 || base > 36) { - pr_crit("number: Unsupported base %d.\n", base); return 0; // Return NULL if the base is invalid. } diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index 280bca16..d1cfd67f 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -13,8 +13,6 @@ set(BUDDY_SYSTEM_FILE ${CMAKE_SOURCE_DIR}/mentos/src/mem/libbuddysystem.a) # Add the memory option. option(USE_BUDDY_SYSTEM "Build using the buddysystem written by the user." OFF) -# Enables cache tracing. -option(ENABLE_CACHE_TRACE "Enables cache tracing." OFF) # Enables memory allocation tracing. option(ENABLE_ALLOC_TRACE "Enables memory allocation tracing." OFF) # Enables scheduling feedback on terminal. @@ -42,10 +40,12 @@ set(KERNEL_SOURCES ${CMAKE_SOURCE_DIR}/mentos/src/fs/vfs.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/read_write.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/open.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/pipe.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/stat.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/readdir.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/procfs.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/ioctl.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/fcntl.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/namei.c ${CMAKE_SOURCE_DIR}/mentos/src/fs/ext2.c ${CMAKE_SOURCE_DIR}/mentos/src/hardware/timer.c @@ -137,18 +137,15 @@ target_compile_definitions( ${KERNEL_NAME} PUBLIC __KERNEL__ ) +target_compile_definitions( + ${KERNEL_NAME} PUBLIC + MENTOS_ROOT="${CMAKE_SOURCE_DIR}" +) # If the emulator is set to output on a log file, tell that to the C code. if(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_LOG) target_compile_definitions(${KERNEL_NAME} PUBLIC EMULATOR_OUTPUT_LOG) endif() - -# ============================================================================= -# Enables cache tracing. -if(ENABLE_CACHE_TRACE) - target_compile_definitions(${KERNEL_NAME} PUBLIC ENABLE_CACHE_TRACE) -endif(ENABLE_CACHE_TRACE) - # ============================================================================= # Enables memory allocation tracing. if(ENABLE_ALLOC_TRACE) diff --git a/mentos/inc/descriptor_tables/isr.h b/mentos/inc/descriptor_tables/isr.h index acb19291..01d92304 100644 --- a/mentos/inc/descriptor_tables/isr.h +++ b/mentos/inc/descriptor_tables/isr.h @@ -64,10 +64,6 @@ int irq_uninstall_handler(unsigned i, interrupt_handler_t handler); /// @param f The interrupt stack frame. extern void irq_handler(pt_regs *f); -/// @brief Method called by CPU to handle exceptions. -/// @param f The interrupt stack frame. -extern void isq_handler(pt_regs *f); - //==== List of exceptions generated internally by the CPU ====================== #define DIVIDE_ERROR 0 ///< DE Divide Error. #define DEBUG_EXC 1 ///< DB Debug. diff --git a/mentos/inc/fs/ioctl.h b/mentos/inc/fs/ioctl.h deleted file mode 100644 index 5b8fd792..00000000 --- a/mentos/inc/fs/ioctl.h +++ /dev/null @@ -1,14 +0,0 @@ -/// @file ioctl.h -/// @brief Declares device controlling operations. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief Manipulates the underlying device parameters of special files, or operating -/// characteristics of character special files (e.g., terminals). -/// @param fd Must be an open file descriptor. -/// @param request The device-dependent request code -/// @param data An untyped pointer to memory. -/// @return On success zero is returned. -int sys_ioctl(int fd, int request, void *data); diff --git a/mentos/inc/fs/pipe.h b/mentos/inc/fs/pipe.h new file mode 100644 index 00000000..0ac6be70 --- /dev/null +++ b/mentos/inc/fs/pipe.h @@ -0,0 +1,115 @@ +/// @file pipe.h +/// @brief PIPE functions and structures declaration. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "process/process.h" +#include "process/wait.h" +#include "klib/mutex.h" + +/// @brief This constant specifies the size of the buffer allocated for each +/// pipe, and its value can affect the performance and capacity of pipes. +// #define PIPE_BUFFER_SIZE PAGE_SIZE +#define PIPE_BUFFER_SIZE 64 + +/// @brief The number of buffers. +#define PIPE_NUM_BUFFERS 5 + +/// @brief Represents a single buffer within a pipe. This structure manages the +/// data stored in the buffer, including its memory location, size, usage count, +/// and associated operations. +typedef struct pipe_buffer { + /// @brief The buffer's data. + char data[PIPE_BUFFER_SIZE]; + + /// @brief Offset within the memory page where the buffer's data begins. + /// This allows for partial usage of the page if the buffer does not occupy + /// the entire page. + size_t offset; + + /// @brief Length of the data currently stored in the buffer. This indicates + /// the amount of data that the buffer holds and can be used during read and + /// write operations. + size_t len; + + /// @brief Pointer to a set of operations that can be performed on the + /// buffer. These operations include functions for getting, releasing, and + /// mapping the buffer, tailored to the specific needs of the buffer's data + /// type. + const struct pipe_buf_operations *ops; + +} pipe_buffer_t; + +/// @brief This structure represents a pipe in the kernel. It contains +/// information about the buffer used for the pipe, the readers and writers, and +/// synchronization details. +typedef struct pipe_inode_info { + /// @brief Array of pipe buffers. Each buffer holds a portion of data for + /// the pipe. For the time beeing I'm setting a fixed number of buffers. + pipe_buffer_t bufs[PIPE_NUM_BUFFERS]; + + /// @brief Number of buffers allocated for the pipe. This value determines + /// the size of the `bufs` array and how many buffers are available for use. + size_t numbuf; + + /// @brief Index for reading. + size_t read_index; + + /// @brief Index for writing. + size_t write_index; + + /// @brief The number of processes currently reading from the pipe. + size_t readers; + + /// @brief The number of processes currently writing to the pipe. + size_t writers; + + /// @brief Wait queue for processes that are blocked waiting to read from + /// the pipe. This queue helps manage process scheduling and + /// synchronization. + wait_queue_head_t read_wait; + + /// @brief Wait queue for processes that are blocked waiting to write from + /// the pipe. This queue helps manage process scheduling and + /// synchronization. + wait_queue_head_t write_wait; + + /// @brief Mutex for protecting access to the pipe structure and ensuring + /// thread-safe operations. This prevents race conditions when multiple + /// processes or threads interact with the pipe. + mutex_t mutex; + + /// @brief List node for tracking this pipe in a process’s list of opened + /// pipes. + list_head list_node; +} pipe_inode_info_t; + +/// @brief Structure defining operations for managing pipe buffers. +typedef struct pipe_buf_operations { + /// @brief Ensures that the buffer is valid and ready for use. + int (*confirm)(pipe_inode_info_t *, size_t); + + /// @brief Checks if the buffer is empty. + int (*empty)(pipe_inode_info_t *, size_t); + + /// @brief Calculates the available data in the buffer. + size_t (*available)(pipe_inode_info_t *, size_t); + + /// @brief Calculates the remaining capacity in the buffer. + size_t (*capacity)(pipe_inode_info_t *, size_t); + + /// @brief Reads data from the buffer into a specified destination. + ssize_t (*read)(pipe_inode_info_t *, size_t, char *, size_t); + + /// @brief Writes data to the buffer from a specified source. + int (*write)(pipe_inode_info_t *, size_t, const char *, size_t); + +} pipe_buf_operations_t; + +/// @brief Updates readers and writers counts for pipes. +/// @param task Pointer to the new task's `task_struct`. +/// @param old_task Pointer to the old task's `task_struct`. +/// @return int 0 on success, 1 on failure. +int vfs_update_pipe_counts(task_struct *task, task_struct *old_task); diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index 67271927..417188e8 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -26,12 +26,12 @@ void vfs_init(void); /// @brief Register a new filesystem. /// @param fs A pointer to the information concerning the new filesystem. /// @return The outcome of the operation, 0 if fails. -int vfs_register_filesystem(file_system_type *fs); +int vfs_register_filesystem(file_system_type_t *fs); /// @brief Unregister a new filesystem. /// @param fs A pointer to the information concerning the filesystem. /// @return The outcome of the operation, 0 if fails. -int vfs_unregister_filesystem(file_system_type *fs); +int vfs_unregister_filesystem(file_system_type_t *fs); /// @brief Register a superblock for the filesystem. /// @param name The name of the superblock. @@ -39,7 +39,7 @@ int vfs_unregister_filesystem(file_system_type *fs); /// @param type A pointer to the filesystem type. /// @param root A pointer to the root file of the filesystem. /// @return 1 on success, 0 on failure. -int vfs_register_superblock(const char *name, const char *path, file_system_type *type, vfs_file_t *root); +int vfs_register_superblock(const char *name, const char *path, file_system_type_t *type, vfs_file_t *root); /// @brief Unregister a superblock. /// @param sb A pointer to the superblock to unregister. @@ -51,6 +51,10 @@ int vfs_unregister_superblock(super_block_t *sb); /// @return Pointer to the super_block_t of the mountpoint, or NULL if not found. super_block_t *vfs_get_superblock(const char *absolute_path); +/// @brief Dumps the list of all superblocks to the log. +/// @param log_level Logging level to use for the output. +void vfs_dump_superblocks(int log_level); + /// @brief Open a file given its absolute path. /// @param absolute_path An absolute path to the file. /// @param flags Used to set the file status flags and access modes. @@ -107,13 +111,19 @@ off_t vfs_lseek(vfs_file_t *file, off_t offset, int whence); /// appropriately. ssize_t vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count); -/// @brief Perform the I/O control operation specified by REQUEST on FD. -/// One argument may follow; its presence and type depend on REQUEST. -/// @param file The file for which we are executing the operations. -/// @param request The device-dependent request code -/// @param data An untyped pointer to memory. -/// @return Return value depends on REQUEST. Usually -1 indicates error. -int vfs_ioctl(vfs_file_t *file, int request, void *data); +/// @brief Perform the I/O control operation specified by `request` on `file`. +/// @param file The file for which the operation is executed. +/// @param request The device-dependent request code. +/// @param data An untyped value or pointer, depending on the request. +/// @return A request-specific return value, or a negative error code on failure. +long vfs_ioctl(vfs_file_t *file, unsigned int request, unsigned long data); + +/// @brief Provides control operations on an open file descriptor. +/// @param file The file for which the operation is executed. +/// @param request The `fcntl` command, defining the operation (e.g., `F_GETFL`, `F_SETFL`). +/// @param data Additional data required by certain `fcntl` commands (e.g., flags or pointer). +/// @return Returns 0 on success; on error, returns a negative error code. +long vfs_fcntl(vfs_file_t *file, unsigned int request, unsigned long data); /// @brief Delete a name and possibly the file it refers to. /// @param path The path to the file. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index 9d091887..61640e4e 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -5,7 +5,7 @@ #pragma once -#include "sys/list_head.h" +#include "list_head.h" #include "bits/stat.h" #include "stdint.h" #include "dirent.h" @@ -15,46 +15,23 @@ #define PATH_UP ".." ///< The path to the parent. #define PATH_DOT "." ///< The path to the current directory. -/// Forward declaration of the VFS file. -typedef struct vfs_file_t vfs_file_t; - -/// Forward declaration of the inode attributes. -struct iattr; - -/// Function used to create a directory. -typedef int (*vfs_mkdir_callback)(const char *, mode_t); -/// Function used to remove a directory. -typedef int (*vfs_rmdir_callback)(const char *); -/// Function used to open a file (or directory). -typedef vfs_file_t *(*vfs_creat_callback)(const char *, mode_t); -/// Function used to read the entries of a directory. -typedef ssize_t (*vfs_getdents_callback)(vfs_file_t *, dirent_t *, off_t, size_t); -/// Function used to open a file (or directory). -typedef vfs_file_t *(*vfs_open_callback)(const char *, int, mode_t); -/// Function used to remove a file. -typedef int (*vfs_unlink_callback)(const char *); -/// Function used to close a file. -typedef int (*vfs_close_callback)(vfs_file_t *); -/// Function used to read from a file. -typedef ssize_t (*vfs_read_callback)(vfs_file_t *, char *, off_t, size_t); -/// Function used to write inside a file. -typedef ssize_t (*vfs_write_callback)(vfs_file_t *, const void *, off_t, size_t); -/// Function used to reposition the file offset inside a file. -typedef off_t (*vfs_lseek_callback)(vfs_file_t *, off_t, int); -/// Function used to stat fs entries. -typedef int (*vfs_stat_callback)(const char *, stat_t *); -/// Function used to stat files. -typedef int (*vfs_fstat_callback)(vfs_file_t *, stat_t *); -/// Function used to perform ioctl on files. -typedef int (*vfs_ioctl_callback)(vfs_file_t *, int, void *); -/// Function for creating symbolic links. -typedef int (*vfs_symlink_callback)(const char *, const char *); -/// Function that reads the symbolic link data associated with a file. -typedef ssize_t (*vfs_readlink_callback)(const char *, char *, size_t); -/// Function used to modify the attributes of an fs entry. -typedef int (*vfs_setattr_callback)(const char *, struct iattr *); -/// Function used to modify the attributes of a file. -typedef int (*vfs_fsetattr_callback)(vfs_file_t *, struct iattr *); +/// @brief Data structure containing attributes of a file. +struct iattr { + /// Validity check on iattr struct. + unsigned int ia_valid; + /// Access mode. + mode_t ia_mode; + /// Owner uid. + uid_t ia_uid; + /// Owner gid. + gid_t ia_gid; + /// Time of last access. + uint32_t ia_atime; + /// Time of last data modification. + uint32_t ia_mtime; + /// Time of last status change. + uint32_t ia_ctime; +}; /// @brief Filesystem information. typedef struct file_system_type { @@ -63,53 +40,57 @@ typedef struct file_system_type { /// Flags of the filesystem. int fs_flags; /// Mount function. - vfs_file_t *(*mount)(const char *, const char *); -} file_system_type; + struct vfs_file *(*mount)(const char *, const char *); + /// List head for linking filesystem types. + struct list_head list; +} file_system_type_t; -/// @brief Set of functions used to perform operations on filesystem. -typedef struct vfs_sys_operations_t { +/// @brief Set of functions used to perform operations on a filesystem. +typedef struct vfs_sys_operations { /// Creates a directory. - vfs_mkdir_callback mkdir_f; + int (*mkdir_f)(const char *, mode_t); /// Removes a directory. - vfs_rmdir_callback rmdir_f; - /// Stat function. - vfs_stat_callback stat_f; - /// File creation function. - vfs_creat_callback creat_f; - /// Symbolic link creation function. - vfs_symlink_callback symlink_f; - /// Modifies the attributes of a file. - vfs_setattr_callback setattr_f; + int (*rmdir_f)(const char *); + /// Retrieves file status information. + int (*stat_f)(const char *, stat_t *); + /// Creates a new file or directory. + struct vfs_file *(*creat_f)(const char *, mode_t); + /// Creates a symbolic link. + int (*symlink_f)(const char *, const char *); + /// Modifies the attributes of a filesystem entry. + int (*setattr_f)(const char *, struct iattr *); } vfs_sys_operations_t; /// @brief Set of functions used to perform operations on files. -typedef struct vfs_file_operations_t { - /// Open a file. - vfs_open_callback open_f; - /// Remove a file. - vfs_unlink_callback unlink_f; - /// Close a file. - vfs_close_callback close_f; - /// Read from a file. - vfs_read_callback read_f; - /// Write inside a file. - vfs_write_callback write_f; - /// Reposition the file offset inside a file. - vfs_lseek_callback lseek_f; - /// Stat the file. - vfs_fstat_callback stat_f; - /// Perform ioctl on file. - vfs_ioctl_callback ioctl_f; - /// Read entries inside the directory. - vfs_getdents_callback getdents_f; - /// Reads the symbolik link data. - vfs_readlink_callback readlink_f; - /// Modifies the attributes of a file. - vfs_fsetattr_callback setattr_f; +typedef struct vfs_file_operations { + /// Opens a file. + struct vfs_file *(*open_f)(const char *, int, mode_t); + /// Removes a file. + int (*unlink_f)(const char *); + /// Closes a file. + int (*close_f)(struct vfs_file *); + /// Reads data from a file. + ssize_t (*read_f)(struct vfs_file *, char *, off_t, size_t); + /// Writes data to a file. + ssize_t (*write_f)(struct vfs_file *, const void *, off_t, size_t); + /// Repositions the file offset within a file. + off_t (*lseek_f)(struct vfs_file *, off_t, int); + /// Retrieves status information of an open file. + int (*stat_f)(struct vfs_file *, stat_t *); + /// Performs an ioctl operation on a file. + long (*ioctl_f)(struct vfs_file *, unsigned int, unsigned long); + /// Performs a fcntl operation on a file. + long (*fcntl_f)(struct vfs_file *, unsigned int, unsigned long); + /// Reads entries within a directory. + ssize_t (*getdents_f)(struct vfs_file *, dirent_t *, off_t, size_t); + /// Reads the target of a symbolic link. + ssize_t (*readlink_f)(const char *, char *, size_t); + /// Modifies the attributes of an open file. + int (*setattr_f)(struct vfs_file *, struct iattr *); } vfs_file_operations_t; /// @brief Data structure that contains information about the mounted filesystems. -struct vfs_file_t { +typedef struct vfs_file { /// The filename. char name[NAME_MAX]; /// Device object (optional). @@ -148,50 +129,32 @@ struct vfs_file_t { uint32_t nlink; /// List to hold all active files associated with a specific entry in a filesystem. list_head siblings; - /// TODO: Comment. + /// Reference count for this file. int32_t refcount; -}; +} vfs_file_t; /// @brief A structure that represents an instance of a filesystem, i.e., a mounted filesystem. -typedef struct super_block_t { +typedef struct super_block { /// Name of the superblock. char name[NAME_MAX]; /// Path of the superblock. char path[PATH_MAX]; /// Pointer to the root file of the given filesystem. - vfs_file_t *root; + struct vfs_file *root; /// Pointer to the information regarding the filesystem. - file_system_type *type; + file_system_type_t *type; /// List to hold all active mounting points. list_head mounts; } super_block_t; /// @brief Data structure containing information about an open file. -typedef struct vfs_file_descriptor_t { +typedef struct vfs_file_descriptor { /// the underlying file structure - vfs_file_t *file_struct; + struct vfs_file *file_struct; /// Flags for file opening modes. int flags_mask; } vfs_file_descriptor_t; -/// @brief Data structure containing attributes of a file. -struct iattr { - /// Validity check on iattr struct. - unsigned int ia_valid; - /// Access mode. - mode_t ia_mode; - /// Owner uid. - uid_t ia_uid; - /// Owner gid. - gid_t ia_gid; - /// Time of last access. - uint32_t ia_atime; - /// Time of last data modification. - uint32_t ia_mtime; - /// Time of last status change. - uint32_t ia_ctime; -}; - #define ATTR_MODE (1 << 0) ///< Flag set to specify the validity of MODE. #define ATTR_UID (1 << 1) ///< Flag set to specify the validity of UID. #define ATTR_GID (1 << 2) ///< Flag set to specify the validity of GID. diff --git a/mentos/inc/hardware/timer.h b/mentos/inc/hardware/timer.h index f390c9fb..31d2dbaa 100644 --- a/mentos/inc/hardware/timer.h +++ b/mentos/inc/hardware/timer.h @@ -7,7 +7,7 @@ #include "kernel.h" #include "stdint.h" -#include "sys/list_head.h" +#include "list_head.h" #include "klib/spinlock.h" #include "process/process.h" #include "time.h" diff --git a/mentos/inc/io/debug.h b/mentos/inc/io/debug.h new file mode 100644 index 00000000..955325cf --- /dev/null +++ b/mentos/inc/io/debug.h @@ -0,0 +1,163 @@ +/// @file debug.h +/// @brief Debugging primitives. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/kernel_levels.h" +#include "string.h" + +#ifndef __DEBUG_LEVEL__ +/// Defines the debug level, by default we set it to notice. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE +#endif + +#ifndef __DEBUG_HEADER__ +/// Header for identifying outputs coming from a mechanism. +#define __DEBUG_HEADER__ 0 +#endif + +/// @brief Sets the loglevel. +/// @param level The new loglevel. +void set_log_level(int level); + +/// @brief Transforms the given amount of bytes to a readable string. +/// @param bytes The bytes to turn to string. +/// @return String representing the bytes in human readable form. +const char *to_human_size(unsigned long bytes); + +/// @brief Transforms the given value to a binary string. +/// @param value to print. +/// @param length of the binary output. +/// @return String representing the binary value. +const char *dec_to_binary(unsigned long value, unsigned length); + +/// @brief Returns the current loglevel. +/// @return The current loglevel +int get_log_level(void); + +/// @brief Prints the given character to debug output. +/// @param c The character to print. +void dbg_putchar(char c); + +/// @brief Prints the given string to debug output. +/// @param s The string to print. +void dbg_puts(const char *s); + +/// @brief Prints the given string to the debug output. +/// @param file the name of the file. +/// @param fun the name of the function. +/// @param line the line inside the file. +/// @param header the header to print. +/// @param log_level the log level. +/// @param format the format to used, see printf. +/// @param ... the list of arguments. +void dbg_printf(const char *file, const char *fun, int line, char *header, short log_level, const char *format, ...); + +/// @brief Extracts the relative path of the current file from the project root. +/// @details This macro calculates the relative path of the file (`__FILE__`) by +/// skipping the prefix defined by `MENTOS_ROOT`. It is used to simplify file +/// path logging by removing the absolute path up to the project root. +/// If +/// MENTOS_ROOT = "/path/to/mentos" and +/// __FILE__ = "/path/to/mentos/src/kernel/main.c", the result will be +/// "src/kernel/main.c". +#define __RELATIVE_PATH__ \ + (strncmp(__FILE__, MENTOS_ROOT, sizeof(MENTOS_ROOT) - 1) == 0 ? (&__FILE__[sizeof(MENTOS_ROOT)]) : __FILE__) + +/// General logging macro that logs a message at the specified log level. +/// Only logs messages if the specified log level is less than or equal to __DEBUG_LEVEL__. +#define pr_log(level, ...) \ + do { \ + if (level <= __DEBUG_LEVEL__) { \ + dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, level, __VA_ARGS__); \ + } \ + } while (0) + +/// Prints a default message, which is always shown. +#define pr_default(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_DEFAULT, __VA_ARGS__) + +/// Prints an emergency message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_EMERG +#define pr_emerg(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_EMERG, __VA_ARGS__) +#else +#define pr_emerg(...) +#endif + +/// Prints an alert message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ALERT +#define pr_alert(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_ALERT, __VA_ARGS__) +#else +#define pr_alert(...) +#endif + +/// Prints a critical message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_CRIT +#define pr_crit(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_CRIT, __VA_ARGS__) +#else +#define pr_crit(...) +#endif + +/// Prints an error message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ERR +#define pr_err(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_ERR, __VA_ARGS__) +#else +#define pr_err(...) +#endif + +/// Prints a warning message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_WARNING +#define pr_warning(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_WARNING, __VA_ARGS__) +#else +#define pr_warning(...) +#endif + +/// Prints a notice message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_NOTICE +#define pr_notice(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_NOTICE, __VA_ARGS__) +#else +#define pr_notice(...) +#endif + +/// Prints a info message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_INFO +#define pr_info(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_INFO, __VA_ARGS__) +#else +#define pr_info(...) +#endif + +/// Prints a debug message. +#if __DEBUG_LEVEL__ >= LOGLEVEL_DEBUG +#define pr_debug(...) dbg_printf(__RELATIVE_PATH__, __func__, __LINE__, __DEBUG_HEADER__, LOGLEVEL_DEBUG, __VA_ARGS__) +#else +#define pr_debug(...) +#endif + +struct pt_regs; +/// @brief Prints the registers using the specified debug function. +/// @param dbg_fn The debug function to use for printing. +/// @param frame The registers to print. +#define PRINT_REGS(dbg_fn, frame) \ + do { \ + dbg_fn("Interrupt stack frame:\n"); \ + dbg_fn("GS = 0x%-04x\n", frame->gs); \ + dbg_fn("FS = 0x%-04x\n", frame->fs); \ + dbg_fn("ES = 0x%-04x\n", frame->es); \ + dbg_fn("DS = 0x%-04x\n", frame->ds); \ + dbg_fn("EDI = 0x%-09x\n", frame->edi); \ + dbg_fn("ESI = 0x%-09x\n", frame->esi); \ + dbg_fn("EBP = 0x%-09x\n", frame->ebp); \ + dbg_fn("ESP = 0x%-09x\n", frame->esp); \ + dbg_fn("EBX = 0x%-09x\n", frame->ebx); \ + dbg_fn("EDX = 0x%-09x\n", frame->edx); \ + dbg_fn("ECX = 0x%-09x\n", frame->ecx); \ + dbg_fn("EAX = 0x%-09x\n", frame->eax); \ + dbg_fn("INT_NO = %-9d\n", frame->int_no); \ + dbg_fn("ERR_CD = %-9d\n", frame->err_code); \ + dbg_fn("EIP = 0x%-09x\n", frame->eip); \ + dbg_fn("CS = 0x%-04x\n", frame->cs); \ + dbg_fn("EFLAGS = 0x%-09x\n", frame->eflags); \ + dbg_fn("UESP = 0x%-09x\n", frame->useresp); \ + dbg_fn("SS = 0x%-04x\n", frame->ss); \ + } while (0) diff --git a/mentos/inc/klib/hashmap.h b/mentos/inc/klib/hashmap.h deleted file mode 100644 index 5c68497e..00000000 --- a/mentos/inc/klib/hashmap.h +++ /dev/null @@ -1,135 +0,0 @@ -/// @file hashmap.h -/// @brief Functions for managing a structure that can map keys to values. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "klib/list.h" - -// == OPAQUE TYPES ============================================================ -/// @brief Stores information of an entry of the hashmap. -typedef struct hashmap_entry_t hashmap_entry_t; -/// @brief Stores information of a hashmap. -typedef struct hashmap_t hashmap_t; - -// == HASHMAP FUNCTIONS ======================================================= -/// @brief Hashing function, used to generate hash keys. -typedef unsigned int (*hashmap_hash_t)(const void *key); -/// @brief Comparison function, used to compare hash keys. -typedef int (*hashmap_comp_t)(const void *a, const void *b); -/// @brief Key duplication function, used to duplicate hash keys. -typedef void *(*hashmap_dupe_t)(const void *); -/// @brief Key deallocation function, used to free the memory occupied by hash keys. -typedef void (*hashmap_free_t)(void *); - -// == HASHMAP KEY MANAGEMENT FUNCTIONS ======================================== -/// @brief Transforms an integer key into a hash key. -/// @param key The integer key. -/// @return The resulting hash key. -unsigned int hashmap_int_hash(const void *key); - -/// @brief Compares two integer hash keys. -/// @param a The first hash key. -/// @param b The second hash key. -/// @return Result of the comparison. -int hashmap_int_comp(const void *a, const void *b); - -/// @brief Transforms a string key into a hash key. -/// @param key The string key. -/// @return The resulting hash key. -unsigned int hashmap_str_hash(const void *key); - -/// @brief Compares two string hash keys. -/// @param a The first hash key. -/// @param b The second hash key. -/// @return Result of the comparison. -int hashmap_str_comp(const void *a, const void *b); - -/// @brief This function can be passed as hashmap_dupe_t, it does nothing. -/// @param value The value to duplicate. -/// @return The duplicated value. -void *hashmap_do_not_duplicate(const void *value); - -/// @brief This function can be passed as hashmap_free_t, it does nothing. -/// @param value The value to free. -void hashmap_do_not_free(void *value); - -// == HASHMAP CREATION AND DESTRUCTION ======================================== -/// @brief User-defined hashmap. -/// @param size Dimension of the hashmap. -/// @param hash_fun The hashing function. -/// @param comp_fun The hash compare function. -/// @param dupe_fun The key duplication function. -/// @param key_free_fun The function used to free memory of keys. -/// @return A pointer to the hashmap. -/// @details -/// (key_free_fun) : No free function. -/// (val_free_fun) : Standard `free` function. -hashmap_t *hashmap_create( - unsigned int size, - hashmap_hash_t hash_fun, - hashmap_comp_t comp_fun, - hashmap_dupe_t dupe_fun, - hashmap_free_t key_free_fun); - -/// @brief Standard hashmap with keys of type (char *). -/// @param size Dimension of the hashmap. -/// @return A pointer to the hashmap. -/// @details -/// (key_free_fun) : Standard `free` function. -/// (val_free_fun) : Standard `free` function. -hashmap_t *hashmap_create_str(unsigned int size); - -/// @brief Standard hashmap with keys of type (char *). -/// @param size Dimension of the hashmap. -/// @return A pointer to the hashmap. -/// @details -/// (key_free_fun) : No free function. -/// (val_free_fun) : Standard `free` function. -hashmap_t *hashmap_create_int(unsigned int size); - -/// @brief Frees the memory of the hashmap. -/// @param map A pointer to the hashmap. -void hashmap_free(hashmap_t *map); - -// == HASHMAP ACCESS FUNCTIONS ================================================ -/// @brief Sets the `value` for the given `key` in the hashmap `map`. -/// @param map The hashmap. -/// @param key The entry key. -/// @param value The entry value. -/// @return NULL on success, a pointer to an already existing entry if fails. -void *hashmap_set(hashmap_t *map, const void *key, void *value); - -/// @brief Access the value for the given key. -/// @param map The hashmap. -/// @param key The key of the entry we are searching. -/// @return The value on success, or NULL on failure. -void *hashmap_get(hashmap_t *map, const void *key); - -/// @brief Removes the entry with the given key. -/// @param map The hashmap. -/// @param key The key of the entry we are searching. -/// @return The value on success, or NULL on failure. -void *hashmap_remove(hashmap_t *map, const void *key); - -/// @brief Checks if the hashmap is empty. -/// @param map The hashmap. -/// @return 1 if empty, 0 otherwise. -int hashmap_is_empty(hashmap_t *map); - -/// @brief Checks if the hashmap contains an entry with the given key. -/// @param map The hashmap. -/// @param key The key of the entry we are searching. -/// @return 1 if the entry is present, 0 otherwise. -int hashmap_has(hashmap_t *map, const void *key); - -/// @brief Provides access to all the keys. -/// @param map The hashmap. -/// @return A list with all the keys, remember to destroy the list. -list_t *hashmap_keys(hashmap_t *map); - -/// @brief Provides access to all the values. -/// @param map The hashmap. -/// @return A list with all the values, remember to destroy the list. -list_t *hashmap_values(hashmap_t *map); diff --git a/mentos/inc/klib/list.h b/mentos/inc/klib/list.h deleted file mode 100644 index a17c28d9..00000000 --- a/mentos/inc/klib/list.h +++ /dev/null @@ -1,138 +0,0 @@ -/// @file list.h -/// @brief An implementation for generic list. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief Represent the node of a list. -typedef struct listnode_t { - /// A pointer to the value. - void *value; - /// The previous node. - struct listnode_t *prev; - /// The next node. - struct listnode_t *next; -} listnode_t; - -/// @brief Represent the list. -typedef struct list_t { - /// The first element of the list. - listnode_t *head; - /// The last element of the list. - listnode_t *tail; - /// The size of the list. - unsigned int size; -} list_t; - -/// @brief Macro used to iterate through a list. -#define listnode_foreach(it, list) \ - for (listnode_t * (it) = (list)->head; (it) != NULL; (it) = (it)->next) - -/// @brief Create a list and set head, tail to NULL, and size to 0. -/// @return The newly created list. -list_t *list_create(void); - -/// @brief Get list size. -/// @param list The list. -/// @return The size of the list. -unsigned int list_size(list_t *list); - -/// @brief Checks if the list is empty. -/// @param list The list. -/// @return 1 if empty, 0 otherwise. -int list_empty(list_t *list); - -/// @brief Insert a value at the front of list. -/// @param list The list. -/// @param value The value to insert. -/// @return The node associated with the inserted value. -listnode_t *list_insert_front(list_t *list, void *value); - -/// @brief Insert a value at the back of list. -/// @param list The list. -/// @param value The value to insert. -/// @return The node associated with the inserted value. -listnode_t *list_insert_back(list_t *list, void *value); - -/// @brief Given a listnode, remove it from list. -/// @param list The list. -/// @param node The node that has to be removed. -/// @return The value associated with the removed node. -void *list_remove_node(list_t *list, listnode_t *node); - -/// @brief Remove a value at the front of list. -/// @param list The list. -/// @return The value associated with the removed node. -void *list_remove_front(list_t *list); - -/// @brief Remove a value at the back of list. -/// @param list The list. -/// @return The value associated with the removed node. -void *list_remove_back(list_t *list); - -/// @brief Searches the node of the list which points at the given value. -/// @param list The list. -/// @param value The value that has to be searched. -/// @return The node associated with the value. -listnode_t *list_find(list_t *list, void *value); - -/// @brief Insert after tail of list(same as insert back). -/// @param list The list. -/// @param value The value to insert. -void list_push_back(list_t *list, void *value); - -/// @brief Remove and return the tail of the list. -/// @param list The list. -/// @return The node that has been removed. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_back(list_t *list); - -/// @brief Insert before head of list(same as insert front). -/// @param list The list. -/// @param value The value to insert. -void list_push_front(list_t *list, void *value); - -/// @brief Remove and return the head of the list. -/// @param list The list. -/// @return The node that has been removed. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_front(list_t *list); - -/// @brief Get the value of the first element but not remove it. -/// @param list The list. -/// @return The value associated with the first node. -void *list_peek_front(list_t *list); - -/// @brief Get the value of the last element but not remove it. -/// @param list The list. -/// @return The value associated with the first node. -void *list_peek_back(list_t *list); - -/// @brief Destroy a list. -/// @param list The list. -void list_destroy(list_t *list); - -/// @brief Checks if the given value is contained inside the list. -/// @param list The list. -/// @param value The value to search. -/// @return -1 if list element is not found, the index otherwise. -int list_get_index_of_value(list_t *list, void *value); - -/// @brief Returns the node at the given index. -/// @param list The list. -/// @param index The index of the desired node. -/// @return A pointer to the node, or NULL otherwise. -listnode_t *list_get_node_by_index(list_t *list, unsigned int index); - -/// @brief Removes a node from the list at the given index. -/// @param list The list. -/// @param index The index of the node we need to remove. -/// @return The value contained inside the node, NULL otherwise. -void *list_remove_by_index(list_t *list, unsigned int index); - -/// @brief Append source at the end of target. -/// @param target Where the element are added. -/// @param source Where the element are removed. -/// @details Beware, source is destroyed. -void list_merge(list_t *target, list_t *source); diff --git a/mentos/inc/klib/ndtree.h b/mentos/inc/klib/ndtree.h deleted file mode 100644 index 66f4a05c..00000000 --- a/mentos/inc/klib/ndtree.h +++ /dev/null @@ -1,197 +0,0 @@ -/// @file ndtree.h -/// @brief N-Dimensional tree. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// ============================================================================ -// Opaque types. - -/// @brief Node of the tree. -typedef struct ndtree_node_t ndtree_node_t; -/// @brief The tree itself. -typedef struct ndtree_t ndtree_t; -/// @brief Iterator for traversing the tree. -typedef struct ndtree_iter_t ndtree_iter_t; - -// ============================================================================ -// Comparison functions. - -/// @brief Function for comparing elements in the tree. -typedef int (*ndtree_tree_cmp_f)(ndtree_t *tree, void *lhs, void *rhs); -/// @brief Callback to call on elements of the tree. -typedef void (*ndtree_tree_node_f)(ndtree_t *tree, ndtree_node_t *node); - -// ============================================================================ -// Node management functions. - -/// @brief Allocate memory for a node. -/// @return Pointer to the allocated node. -ndtree_node_t *ndtree_node_alloc(void); - -/// @brief Allocate memory for a node and sets its value. -/// @param value Value to associated node. -/// @return Pointer to the allocated node. -ndtree_node_t *ndtree_node_create(void *value); - -/// @brief Initializes the already allocated node. -/// @param node The node itself. -/// @param value The value associated to the node. -/// @return Pointer to the node itself. -ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value); - -/// @brief Sets the value of the given node. -/// @param node The node to manipulate. -/// @param value The value associated to the node. -void ndtree_node_set_value(ndtree_node_t *node, void *value); - -/// @brief Provides access to the value associated to a node. -/// @param node The node itself. -/// @return The value associated to the node. -void *ndtree_node_get_value(ndtree_node_t *node); - -/// @brief Sets the given node as root of the tree. -/// @param tree The tree. -/// @param node The node to set as root. -void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node); - -/// @brief Creates a new node and assigns it as root of the tree. -/// @param tree The tree. -/// @param value The value associated to the node. -/// @return The newly created node. -ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value); - -/// @brief Provides access to the root of the tree. -/// @param tree The tree. -/// @return Pointer to the node. -ndtree_node_t *ndtree_get_root(ndtree_t *tree); - -/// @brief Adds the given `child` as child of `parent`. -/// @param tree The tree. -/// @param parent The `parent` node. -/// @param child The new `child` node. -void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child); - -/// @brief Creates a new node and sets it as child of `parent`. -/// @param tree The tree. -/// @param parent The `parent` node. -/// @param value Value associated with the new child. -/// @return Pointer to the newly created child node. -ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value); - -/// @brief Counts the number of children of the given node. -/// @param node The node of which we count the children. -/// @return The number of children. -unsigned int ndtree_node_count_children(ndtree_node_t *node); - -/// @brief Deallocate a node. -/// @param node The node to destroy. -void ndtree_node_dealloc(ndtree_node_t *node); - -// ============================================================================ -// Tree management functions. - -/// @brief Allocate memory for a tree. -/// @return Pointer to the allocated tree. -ndtree_t *ndtree_tree_alloc(void); - -/// @brief Allocate memory for a tree and sets the function used to compare nodes. -/// @param cmp Function used to compare elements of the tree. -/// @return Pointer to the allocated tree. -ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp); - -/// @brief Deallocate a node. -/// @param tree The tree to destroy. -/// @param node_cb The function called on each element of the tree before destroying the tree. -void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb); - -/// @brief Initializes the tree. -/// @param tree The tree to initialize. -/// @param cmp The compare function to associate to the tree. -/// @return Pointer to tree itself. -ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f cmp); - -/// @brief Searches the node inside the tree with the given value. -/// @param tree The tree. -/// @param cmp The node compare function. -/// @param value The value to search. -/// @return Node associated with the value. -ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value); - -/// @brief Searches the given value among the children of node. -/// @param tree The tree. -/// @param node The node under which we search. -/// @param cmp The node compare function. -/// @param value The value to search. -/// @return Node associated with the value. -ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value); - -/// @brief Returns the size of the tree. -/// @param tree The tree. -/// @return The size of the tree. -unsigned int ndtree_tree_size(ndtree_t *tree); - -/// @brief Removes the node from the given tree. -/// @param tree The tree. -/// @param node The node to remove. -/// @param node_cb The function called on the node before removing it. -/// @return Returns 1 if the node was removed, 0 otherwise. -/// @details -/// Optionally the node callback can be provided to dealloc node and/or -/// user data. Use ndtree_tree_node_dealloc default callback to deallocate -/// node created by ndtree_tree_insert(...). -int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb); - -/// @brief Removes the node from the given tree. -/// @param tree The tree. -/// @param value The value to search. -/// @param node_cb The function called on the node before removing it. -/// @return Returns 1 if the value was removed, 0 otherwise. -/// @details -/// Optionally the node callback can be provided to dealloc node and/or -/// user data. Use ndtree_tree_node_dealloc default callback to deallocate -/// node created by ndtree_tree_insert(...). -int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb); - -// ============================================================================ -// Iterators. - -/// @brief Allocate the memory for the iterator. -/// @return Pointer to the allocated iterator. -ndtree_iter_t *ndtree_iter_alloc(void); - -/// @brief Deallocate the memory for the iterator. -/// @param iter Pointer to the allocated iterator. -void ndtree_iter_dealloc(ndtree_iter_t *iter); - -/// @brief Initializes the iterator the the first child of the node. -/// @param node The node of which we want to iterate the children. -/// @param iter The iterator we want to initialize. -/// @return Pointer to the first node of the list. -ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter); - -/// @brief Initializes the iterator the the last child of the node. -/// @param node The node of which we want to iterate the children. -/// @param iter The iterator we want to initialize. -/// @return Pointer to the last node of the list. -ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter); - -/// @brief Moves the iterator to the next element. -/// @param iter The iterator. -/// @return Pointer to the next element. -ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter); - -/// @brief Moves the iterator to the previous element. -/// @param iter The iterator. -/// @return Pointer to the previous element. -ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter); - -// ============================================================================ -// Tree visiting functions. - -/// @brief Run a visit of the tree (DFS). -/// @param tree The tree to visit. -/// @param enter_fun Function to call when entering a node. -/// @param exit_fun Function to call when exiting a node. -void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun); diff --git a/mentos/inc/klib/stdatomic.h b/mentos/inc/klib/stdatomic.h index 5bbd776c..369f13d9 100644 --- a/mentos/inc/klib/stdatomic.h +++ b/mentos/inc/klib/stdatomic.h @@ -7,8 +7,8 @@ #include "klib/compiler.h" -/// @brief Standard structure for atomic operations (see below -/// for volatile explanation). +/// @brief Standard structure for atomic operations (see below for volatile +/// explanation). typedef volatile unsigned atomic_t; /// @brief The prefix used to lock. @@ -26,25 +26,47 @@ typedef volatile unsigned atomic_t; : \ : "memory") -/// @brief Atomically sets `value` at `ptr`. -/// @param ptr the pointer we are working with. -/// @param value the value to set. -/// @return The final value of the atomic variable. -inline static int atomic_set_and_test(atomic_t *ptr, int value) +/// @brief Atomically compares and exchanges a value. +/// @details If *ptr equals old_val, this function sets *ptr to new_val. Used +/// for conditional updates in lock-free data structures. +/// @param[in,out] ptr Pointer to the atomic variable. +/// @param[in] old_val Expected current value of *ptr. +/// @param[in] new_val Value to set if *ptr equals old_val. +/// @return The original value of *ptr. If this equals old_val, the exchange was +/// successful. +static inline atomic_t atomic_cmpxchg_and_test(volatile atomic_t *ptr, atomic_t old_val, atomic_t new_val) { - // The + in "+r" and "+m" denotes a read-modify-write operand. - __asm__ __volatile__(LOCK_PREFIX // Lock - "xchgl %0, %1" // Instruction - : "+r"(value), "+m"(*ptr) // Input + Output - : // No input-only - : "memory"); // Side effects - return value; + atomic_t prev; + __asm__ __volatile__( + LOCK_PREFIX + "cmpxchgl %2, %1" // Compare *ptr with %eax; if equal, set to new_val + : "=a"(prev), "+m"(*ptr) // Output: prev holds *ptr, *ptr modified if equal + : "r"(new_val), "0"(old_val) // Inputs: new_val, old_val in %eax + : "memory"); // Clobbered: memory to prevent reordering + return prev; // Return the original value of *ptr +} + +/// @brief Atomically sets *ptr to a given value. +/// @details This function sets *ptr to value unconditionally and returns the +/// original value. Useful for atomic resets and state changes. +/// @param ptr Pointer to the atomic variable. +/// @param value New value to set at *ptr. +/// @return The previous value of *ptr. +static inline int atomic_set_and_test(atomic_t *ptr, int value) +{ + __asm__ __volatile__( + LOCK_PREFIX // Lock prefix for atomicity + "xchgl %0, %1" // Exchange value with *ptr + : "+r"(value), "+m"(*ptr) // Input + Output + : // No additional input-only constraints + : "memory"); // Clobbers memory to prevent reordering + return value; // Return the original value of *ptr } /// @brief Atomically set the value pointed by `ptr` to `value`. /// @param ptr the pointer we are working with. /// @param value the value we need to set. -inline static void atomic_set(atomic_t *ptr, int value) +static inline void atomic_set(atomic_t *ptr, int value) { atomic_set_and_test(ptr, value); } @@ -52,7 +74,7 @@ inline static void atomic_set(atomic_t *ptr, int value) /// @brief Atomically read the value pointed by `ptr`. /// @param ptr the pointer we are working with. /// @return the value we read. -inline static int atomic_read(const atomic_t *ptr) +static inline int atomic_read(const atomic_t *ptr) { return READ_ONCE(*ptr); } @@ -61,7 +83,7 @@ inline static int atomic_read(const atomic_t *ptr) /// @param ptr the pointer we are working with. /// @param value the value we need to add. /// @return the result of the operation. -inline static int atomic_add(atomic_t *ptr, int value) +static inline int atomic_add(atomic_t *ptr, int value) { // The + in "+r" and "+m" denotes a read-modify-write operand. __asm__ __volatile__(LOCK_PREFIX // Lock @@ -76,7 +98,7 @@ inline static int atomic_add(atomic_t *ptr, int value) /// @param ptr the pointer we are working with. /// @param value the value we need to subtract. /// @return the result of the operation. -inline static int atomic_sub(atomic_t *ptr, int value) +static inline int atomic_sub(atomic_t *ptr, int value) { return atomic_add(ptr, -value); } @@ -84,7 +106,7 @@ inline static int atomic_sub(atomic_t *ptr, int value) /// @brief Atomically increment the value at `ptr`. /// @param ptr the pointer we are working with. /// @return the result of the operation. -inline static int atomic_inc(atomic_t *ptr) +static inline int atomic_inc(atomic_t *ptr) { return atomic_add(ptr, 1); } @@ -92,7 +114,7 @@ inline static int atomic_inc(atomic_t *ptr) /// @brief Atomically decrement the value at `ptr`. /// @param ptr the pointer we are working with. /// @return the result of the operation. -inline static int atomic_dec(atomic_t *ptr) +static inline int atomic_dec(atomic_t *ptr) { return atomic_add(ptr, -1); } @@ -101,7 +123,7 @@ inline static int atomic_dec(atomic_t *ptr) /// @param ptr the pointer we are working with. /// @param value the value we need to add. /// @return true if the result is negative, false otherwise. -inline static int atomic_add_negative(atomic_t *ptr, int value) +static inline int atomic_add_negative(atomic_t *ptr, int value) { return atomic_add(ptr, value) < 0; } @@ -110,7 +132,7 @@ inline static int atomic_add_negative(atomic_t *ptr, int value) /// @param ptr the pointer we are working with. /// @param value the value we need to subtract. /// @return true if the result is zero, false otherwise. -inline static int atomic_sub_and_test(atomic_t *ptr, int value) +static inline int atomic_sub_and_test(atomic_t *ptr, int value) { return atomic_sub(ptr, value) == 0; } @@ -118,7 +140,7 @@ inline static int atomic_sub_and_test(atomic_t *ptr, int value) /// @brief Atomically increment `ptr` and checks if the result is zero. /// @param ptr the pointer we are working with. /// @return true if the result is zero, false otherwise. -inline static int atomic_inc_and_test(atomic_t *ptr) +static inline int atomic_inc_and_test(atomic_t *ptr) { return atomic_inc(ptr) == 0; } @@ -126,7 +148,7 @@ inline static int atomic_inc_and_test(atomic_t *ptr) /// @brief Atomically decrement `ptr` and checks if the result is zero. /// @param ptr the pointer we are working with. /// @return true if the result is zero, false otherwise. -inline static int atomic_dec_and_test(atomic_t *ptr) +static inline int atomic_dec_and_test(atomic_t *ptr) { return atomic_dec(ptr) == 0; } diff --git a/mentos/inc/mem/buddysystem.h b/mentos/inc/mem/buddysystem.h index 90d31d81..a0e2474f 100644 --- a/mentos/inc/mem/buddysystem.h +++ b/mentos/inc/mem/buddysystem.h @@ -5,7 +5,7 @@ #pragma once -#include "sys/list_head.h" +#include "list_head.h" #include "klib/stdatomic.h" #include "stdint.h" diff --git a/mentos/inc/mem/slab.h b/mentos/inc/mem/slab.h index a08a1787..55c59432 100644 --- a/mentos/inc/mem/slab.h +++ b/mentos/inc/mem/slab.h @@ -5,7 +5,7 @@ #pragma once -#include "sys/list_head.h" +#include "list_head.h" #include "stddef.h" #include "mem/gfp.h" @@ -25,33 +25,33 @@ typedef void (*kmem_fun_t)(void *); /// @brief Stores the information of a cache. typedef struct kmem_cache_t { - /// Handler for placing it inside a lists of caches. + /// Link to place this cache in a global list of caches. list_head cache_list; /// Name of the cache. const char *name; - /// Size of the cache. - unsigned int size; - /// Size of the objects contained in the cache. - unsigned int object_size; - /// Alignment requirement of the type of objects. + /// Total size of each object in the cache, including alignment and padding. + unsigned int aligned_object_size; + /// Original, unaligned size of the objects requested by the user. + unsigned int raw_object_size; + /// Alignment requirement for objects in the cache. unsigned int align; - /// The total number of slabs. + /// Total number of objects allocated across all slabs. unsigned int total_num; - /// The number of free slabs. + /// Number of free objects available across all slabs. unsigned int free_num; - /// The Get Free Pages (GFP) flags. + /// Flags for page allocation behavior. slab_flags_t flags; - /// The order for getting free pages. + /// Page allocation order (power of 2 pages) used for slab allocation. unsigned int gfp_order; - /// Constructor for the elements. + /// Constructor function for initializing objects. kmem_fun_t ctor; - /// Destructor for the elements. + /// Destructor function for cleaning up objects. kmem_fun_t dtor; - /// Handler for the full slabs list. + /// List of fully occupied slabs. list_head slabs_full; - /// Handler for the partial slabs list. + /// List of partially occupied slabs. list_head slabs_partial; - /// Handler for the free slabs list. + /// List of completely free slabs. list_head slabs_free; } kmem_cache_t; @@ -91,8 +91,6 @@ kmem_cache_t *kmem_cache_create( /// @return Returns 0 on success, or -1 if an error occurs. int kmem_cache_destroy(kmem_cache_t *cachep); -#ifdef ENABLE_CACHE_TRACE - /// @brief Allocs a new object using the provided cache. /// @param file File where the object is allocated. /// @param fun Function where the object is allocated. @@ -107,29 +105,8 @@ void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cach /// @param fun Function where the object is deallocated. /// @param line Line inside the file. /// @param addr Address of the object. -void pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr); - -/// Wrapper that provides the filename, the function and line where the alloc is happening. -#define kmem_cache_alloc(...) pr_kmem_cache_alloc(__FILE__, __func__, __LINE__, __VA_ARGS__) - -/// Wrapper that provides the filename, the function and line where the free is happening. -#define kmem_cache_free(...) pr_kmem_cache_free(__FILE__, __func__, __LINE__, __VA_ARGS__) - -#else - -/// @brief Allocates an object from the specified kmem_cache_t. -/// @param cachep Pointer to the cache from which to allocate the object. -/// @param flags Flags for the allocation (e.g., GFP_KERNEL). -/// @return Pointer to the allocated object, or NULL if allocation fails. -void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags); - -/// @brief Frees an object previously allocated from a kmem_cache_t. -/// @param addr Pointer to the object to free. -void kmem_cache_free(void *addr); - -#endif - -#ifdef ENABLE_ALLOC_TRACE +/// @return 0 on success, 1 on error. +int pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr); /// @brief Provides dynamically allocated memory in kernel space. /// @param file File where the object is allocated. @@ -143,24 +120,17 @@ void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size) /// @param file File where the object is deallocated. /// @param fun Function where the object is deallocated. /// @param line Line inside the file. -/// @param ptr The pointer to the allocated memory. +/// @param addr The pointer to the allocated memory. void pr_kfree(const char *file, const char *fun, int line, void *addr); /// Wrapper that provides the filename, the function and line where the alloc is happening. -#define kmalloc(...) pr_kmalloc(__FILE__, __func__, __LINE__, __VA_ARGS__) +#define kmem_cache_alloc(...) pr_kmem_cache_alloc(__FILE__, __func__, __LINE__, __VA_ARGS__) /// Wrapper that provides the filename, the function and line where the free is happening. -#define kfree(...) pr_kfree(__FILE__, __func__, __LINE__, __VA_ARGS__) - -#else - -/// @brief Allocates memory of the specified size using kmalloc. -/// @param size Size of the memory to allocate. -/// @return Pointer to the allocated memory, or NULL if allocation fails. -void *kmalloc(unsigned int size); +#define kmem_cache_free(...) pr_kmem_cache_free(__FILE__, __func__, __LINE__, __VA_ARGS__) -/// @brief Frees memory allocated by kmalloc or kmem_cache_alloc. -/// @param ptr Pointer to the memory to free. -void kfree(void *ptr); +/// Wrapper that provides the filename, the function and line where the alloc is happening. +#define kmalloc(...) pr_kmalloc(__FILE__, __func__, __LINE__, __VA_ARGS__) -#endif +/// Wrapper that provides the filename, the function and line where the free is happening. +#define kfree(...) pr_kfree(__FILE__, __func__, __LINE__, __VA_ARGS__) diff --git a/mentos/inc/mem/zone_allocator.h b/mentos/inc/mem/zone_allocator.h index 7c3a869b..8ef9eb69 100644 --- a/mentos/inc/mem/zone_allocator.h +++ b/mentos/inc/mem/zone_allocator.h @@ -8,7 +8,7 @@ #include "mem/gfp.h" #include "math.h" #include "stdint.h" -#include "sys/list_head.h" +#include "list_head.h" #include "sys/bitops.h" #include "klib/stdatomic.h" #include "boot.h" diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index 8e86dc05..0d4d30d4 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -175,3 +175,10 @@ int init_tasking(void); /// @param path Path of the `init` program. /// @return Pointer to init process. task_struct *process_create_init(const char *path); + +/// @brief Get a file structure from a file descriptor. +/// @param fd the file descriptor. +/// @return Returns the file structure corresponding to the given file +/// descriptor or NULL if the file descriptor is invalid or the file has been +/// closed. +vfs_file_descriptor_t *fget(int fd); diff --git a/mentos/inc/process/scheduler.h b/mentos/inc/process/scheduler.h index 5a969aff..0710e2f1 100644 --- a/mentos/inc/process/scheduler.h +++ b/mentos/inc/process/scheduler.h @@ -5,7 +5,7 @@ #pragma once -#include "sys/list_head.h" +#include "list_head.h" #include "process/process.h" #include "stddef.h" diff --git a/mentos/inc/process/wait.h b/mentos/inc/process/wait.h index 4d88b13f..c587d4bb 100644 --- a/mentos/inc/process/wait.h +++ b/mentos/inc/process/wait.h @@ -5,7 +5,7 @@ #pragma once -#include "sys/list_head.h" +#include "list_head.h" #include "klib/spinlock.h" /// @brief Return immediately if no child is there to be waited for. @@ -77,8 +77,14 @@ typedef struct wait_queue_entry_t { int (*func)(struct wait_queue_entry_t *wait, unsigned mode, int sync); /// Handler for placing the entry inside a waiting queue double linked-list. struct list_head task_list; + /// Additional context or data, typically a pointer to relevant information + /// for the wake function. + void *private; } wait_queue_entry_t; +/// @brief Initializes a wait queue head. +/// @param head Pointer to the wait queue head to initialize. +void wait_queue_head_init(wait_queue_head_t *head); /// @brief Allocates the memory for a wait_queue_entry. /// @return a pointer to the allocated wait_queue_entry. @@ -91,7 +97,7 @@ void wait_queue_entry_dealloc(wait_queue_entry_t * wait_queue_entry); /// @brief Initialize the waiting queue entry. /// @param wq The entry we initialize. /// @param task The task associated with the entry. -void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task); +void wait_queue_entry_init(wait_queue_entry_t *wq, struct task_struct *task); /// @brief Adds the element to the waiting queue. /// @param head The head of the waiting queue. diff --git a/mentos/inc/system/printk.h b/mentos/inc/system/printk.h index 759c1ce9..9383cdc4 100644 --- a/mentos/inc/system/printk.h +++ b/mentos/inc/system/printk.h @@ -5,8 +5,10 @@ #pragma once -/// @brief Write formatted output to stdout. -/// @param format Output formatted as for printf. -/// @param ... List of arguments. -/// @return The number of bytes written in syslog. -int sys_syslog(const char *format, ...); +/// @brief Sends a message to the system log using a specified log level. +/// @param file the name of the file. +/// @param fun the name of the function. +/// @param line the line inside the file. +/// @param log_level the log level. +/// @param format the format to used, see printf. +void sys_syslog(const char *file, const char *fun, int line, short log_level, const char *format); diff --git a/mentos/inc/system/signal.h b/mentos/inc/system/signal.h index 8f51c27b..53ec58ba 100644 --- a/mentos/inc/system/signal.h +++ b/mentos/inc/system/signal.h @@ -7,7 +7,7 @@ #include "klib/stdatomic.h" #include "klib/spinlock.h" -#include "sys/list_head.h" +#include "list_head.h" #include "system/syscall.h" /// @brief Signal codes. @@ -244,15 +244,14 @@ typedef struct sigpending_t { #define SEND_SIG_NOINFO ((siginfo_t *)0) /// @brief Handle the return from a signal handler. -/// @param f The stack frame when returning from a signal handler. -/// @return never. +/// @param f Pointer to the pt_regs structure. +/// @return This function does not return (never). long sys_sigreturn(struct pt_regs *f); /// @brief Handles the signals of the current process. -/// @param f The address of the stack area where the User Mode register -/// contents of the current process are saved. -/// @return If we are handling a signal, thus, `regs` have been modified -/// to handle it (e.g., eip is now poiting at the handler). +/// @param f Pointer to the pt_regs structure. +/// @return 1 if a signal is being handled and registers have been modified; +/// otherwise, 0. int do_signal(struct pt_regs *f); /// @brief Initialize the signals. diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index 37b40f9f..81c8c26b 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -22,10 +22,6 @@ void syscall_init(void); /// @param f The interrupt stack frame. void syscall_handler(pt_regs *f); -/// @brief Returns the current interrupt stack frame. -/// @return Pointer to the stack frame. -pt_regs *get_current_interrupt_stack_frame(void); - /// The exit() function causes normal process termination. /// @param exit_code The exit code. void sys_exit(int exit_code); @@ -385,3 +381,22 @@ int sys_munmap(void *addr, size_t length); /// @param buf Buffer where the info will be placed. /// @return 0 on success, a negative value on failure. int sys_uname(utsname_t *buf); + +/// @brief System call to create a new pipe. +/// @param fds Array to store read and write file descriptors. +/// @return 0 on success, or -1 on error. +int sys_pipe(int fds[2]); + +/// @brief Executes a device-specific control operation on a file descriptor. +/// @param fd The file descriptor for the device or file being operated on. +/// @param request The `ioctl` command, defining the action or configuration. +/// @param data Additional data needed for the `ioctl` command, often a pointer to user-provided data. +/// @return Returns 0 on success; on error, returns a negative error code. +long sys_ioctl(int fd, unsigned int request, unsigned long data); + +/// @brief Provides control operations on an open file descriptor. +/// @param fd The file descriptor on which to perform the operation. +/// @param request The `fcntl` command, defining the operation (e.g., `F_GETFL`, `F_SETFL`). +/// @param data Additional data required by certain `fcntl` commands (e.g., flags or pointer). +/// @return Returns 0 on success; on error, returns a negative error code. +long sys_fcntl(int fd, unsigned int request, unsigned long data); diff --git a/mentos/src/descriptor_tables/exception.c b/mentos/src/descriptor_tables/exception.c index d39187d1..88e64eef 100644 --- a/mentos/src/descriptor_tables/exception.c +++ b/mentos/src/descriptor_tables/exception.c @@ -12,6 +12,7 @@ #include "system/panic.h" #include "descriptor_tables/isr.h" #include "descriptor_tables/idt.h" +#include "process/scheduler.h" #include "stdio.h" /// @brief Default error messages for exceptions. @@ -60,16 +61,42 @@ static char *isr_routines_description[IDT_SIZE]; static inline void default_isr_handler(pt_regs *f) { uint32_t irq_line = f->int_no; + PRINT_REGS(pr_emerg, f); + pr_emerg("No handler for execption: %d (%s)\n", + irq_line, + (irq_line < 32) ? exception_messages[irq_line] : "no description"); + kernel_panic("Missing ISR handler."); +} - dbg_print_regs(f); - - printf("Kernel PANIC!\n no handler for execption [%d]\n", irq_line); - printf("Description: %s\n", (irq_line < 32) ? exception_messages[irq_line] : "no description"); - // Stop kernel execution. - - kernel_panic("Kernel PANIC!\n no handler for execption"); +/// @brief Handles a General Protection Fault (exception 13). +/// @param frame The CPU registers at the time of the exception. +void handle_gp_fault(pt_regs *frame) +{ + // Log the general protection fault details + pr_info("General Protection Fault (Exception 13) occurred!\n"); + pr_info("Faulting address: 0x%-09x\n", frame->eip); + pr_info("Error code: 0x%-04x\n", frame->err_code); - while (1) {} + // Check if the privilege level is 3 (user mode) + if ((frame->cs & 0x3) == 0x3) { + pr_info("Process terminated due to General Protection Fault.\n"); + // Get the current process. + task_struct *task = scheduler_get_current_process(); + assert(task && "There is no current task."); + // Attempt to recover by terminating the user-mode process. + sys_kill(task->pid, SIGSEGV); + // Now, we know the process needs to be removed from the list of + // running processes. We pushed the SEGV signal in the queues of + // signal to send to the process. To properly handle the signal, + // just run scheduler. + scheduler_run(frame); + } else { + // Print all register values for debugging. + PRINT_REGS(pr_crit, frame); + pr_crit("Kernel mode fault. System halt.\n"); + // Here, we halt the CPU to prevent further damage. + kernel_panic("General protection fault."); + } } /// @brief Interrupt Service Routines handler called from the assembly. @@ -91,6 +118,7 @@ void isrs_init(void) for (uint32_t i = 0; i < IDT_SIZE; ++i) { isr_routines[i] = default_isr_handler; } + isr_routines[13] = handle_gp_fault; } int isr_install_handler(unsigned i, interrupt_handler_t handler, char *description) diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index 02ee164d..bdd7e1d3 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -25,7 +25,7 @@ #include "process/wait.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/panic.h" /// @brief IDENTIFY device data (response to 0xEC). @@ -705,12 +705,12 @@ static inline int ata_dma_free(uintptr_t logical_addr) // Free the allocated pages. if (__free_pages(page) < 0) { - pr_debug("Failed to free allocated pages 0x%p with order %d.\n", page, order); + pr_debug("Failed to free allocated pages 0x%p.\n", page); return 1; } // Debugging information. - pr_debug("Successfully freed DMA memory at logical address 0x%lx with order %d.\n", logical_addr, order); + pr_debug("Successfully freed DMA memory at logical address 0x%p.\n", logical_addr); return 0; // Success. } @@ -1269,37 +1269,39 @@ static vfs_file_t *ata_open(const char *path, int flags, mode_t mode) /// @brief Closes an ATA device. /// @param file the VFS file associated with the ATA device. -/// @return 0 on success, it panics on failure. +/// @return 0 on success, -errno on failure. static int ata_close(vfs_file_t *file) { - pr_debug("ata_close(%p)\n", file); - - // Check if the file pointer is NULL. + // Validate the file pointer. if (file == NULL) { - pr_crit("Attempted to close a NULL file pointer.\n"); - return 1; + pr_err("ata_close: Invalid file pointer (NULL).\n"); + return -EINVAL; } // Get the device from the VFS file. ata_device_t *dev = (ata_device_t *)file->device; - - // Check the device. if (dev == NULL) { - pr_crit("Device not set for file: %p\n", file); - return 1; + pr_crit("ata_close: Device not set for file `%s`.\n", file->name); + return -ENODEV; } - // Check if the device is one of the ATA devices. - if ((dev == &ata_primary_master) || (dev == &ata_primary_slave) || - (dev == &ata_secondary_master) || (dev == &ata_secondary_slave)) { - // Decrement the reference count for the file. - if (--file->count == 0) { - // Optional: Free any resources if this is the last reference. - // Freeing resources can be added here if necessary. - } - } else { - pr_crit("Invalid device encountered: %p\n", dev); - return 1; + // Ensure the device is one of the known ATA devices. + if (!(dev == &ata_primary_master || dev == &ata_primary_slave || dev == &ata_secondary_master || dev == &ata_secondary_slave)) { + pr_crit("ata_close: Invalid device encountered for file `%s`.\n", file->name); + return -EINVAL; + } + + // Decrement the reference count for the file. + if (--file->count == 0) { + pr_debug("ata_close: Closing file `%s` (ino: %d).\n", file->name, file->ino); + + // Remove the file from the list of opened files. + list_head_remove(&file->siblings); + pr_debug("ata_close: Removed file `%s` from the opened file list.\n", file->name); + + // Free the file from cache. + kmem_cache_free(file); + pr_debug("ata_close: Freed memory for file `%s`.\n", file->name); } return 0; @@ -1515,7 +1517,7 @@ static vfs_file_t *ata_mount_callback(const char *path, const char *device) } /// Filesystem information. -static file_system_type ata_file_system_type = { +static file_system_type_t ata_file_system_type = { .name = "ata", .fs_flags = 0, .mount = ata_mount_callback diff --git a/mentos/src/drivers/mem.c b/mentos/src/drivers/mem.c index ae9b1623..44d28e79 100644 --- a/mentos/src/drivers/mem.c +++ b/mentos/src/drivers/mem.c @@ -14,7 +14,7 @@ #include "io/debug.h" #include "fs/vfs.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/syscall.h" #include "process/scheduler.h" #include "fcntl.h" @@ -99,7 +99,7 @@ static ssize_t null_read(vfs_file_t *file, char *buffer, off_t offset, size_t si static int null_fstat(vfs_file_t *file, stat_t *stat); /// @brief Filesystem type structure for the null device. -static file_system_type null_file_system_type = { +static file_system_type_t null_file_system_type = { .name = "null", .fs_flags = 0, .mount = null_mount_callback @@ -166,10 +166,6 @@ static vfs_file_t *null_mount_callback(const char *path, const char *device) } /// @brief Creates a null memory device. -/// -/// @details This function allocates memory for a new null device and its associated -/// file, initializes the file structure, and sets appropriate operations. -/// /// @param name The name of the null device to be created. /// @return A pointer to the newly created memory device, or NULL if creation fails. static struct memdev *null_device_create(const char *name) @@ -181,39 +177,38 @@ static struct memdev *null_device_create(const char *name) } // Allocate memory for the new device. - struct memdev *dev = kmalloc(sizeof(struct memdev)); + struct memdev *dev = (struct memdev *)kmalloc(sizeof(struct memdev)); if (dev == NULL) { pr_err("null_device_create: Failed to allocate memory for device\n"); return NULL; } dev->next = NULL; - // Allocate memory for the associated file. - vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); - if (file == NULL) { + // Allocate memory for the associated file structure. + dev->file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (dev->file == NULL) { pr_err("null_device_create: Failed to allocate memory for file\n"); kfree(dev); // Free the previously allocated device memory. return NULL; } - dev->file = file; // Set the device name, ensuring it doesn't exceed NAME_MAX. - strncpy(file->name, name, NAME_MAX - 1); - file->name[NAME_MAX - 1] = '\0'; // Ensure null termination. + strncpy(dev->file->name, name, NAME_MAX - 1); + dev->file->name[NAME_MAX - 1] = '\0'; // Ensure null termination. // Initialize file fields. - file->count = 0; - file->uid = 0; - file->gid = 0; - file->mask = 0x2000 | 0666; // Regular file with rw-rw-rw- permissions. - file->atime = sys_time(NULL); // Set access time. - file->mtime = sys_time(NULL); // Set modification time. - file->ctime = sys_time(NULL); // Set change time. - file->length = 0; // Initialize file length to 0. + dev->file->count = 0; + dev->file->uid = 0; + dev->file->gid = 0; + dev->file->mask = 0x2000 | 0666; // Regular file with rw-rw-rw- permissions. + dev->file->atime = sys_time(NULL); // Set access time. + dev->file->mtime = sys_time(NULL); // Set modification time. + dev->file->ctime = sys_time(NULL); // Set change time. + dev->file->length = 0; // Initialize file length to 0. // Set the file system and system operations for the file. - file->sys_operations = &mem_sys_operations; - file->fs_operations = &null_fs_operations; + dev->file->sys_operations = &mem_sys_operations; + dev->file->fs_operations = &null_fs_operations; return dev; } @@ -271,26 +266,34 @@ static vfs_file_t *null_open(const char *path, int flags, mode_t mode) } /// @brief Closes a null device file. -/// /// @param file A pointer to the vfs_file_t structure representing the file to close. -/// @return 0 on success, -EINVAL if the file is NULL. +/// @return 0 on success, -errno on failure. static int null_close(vfs_file_t *file) { - // Check if the file is NULL. + // Check if the file pointer is NULL. if (file == NULL) { - pr_crit("null_close: Received NULL file\n"); - return -EINVAL; // Return an error if the file is NULL. + pr_crit("null_close: Received NULL file pointer.\n"); + return -EINVAL; } - // Decrease the reference count. - if (file->count > 0) { - file->count--; - } else { - pr_warning("null_close: Attempt to close a file with zero reference count\n"); + pr_debug("null_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count); + + // Decrement the reference count for the file. + if (--file->count == 0) { + pr_debug("null_close: Closing file `%s` (ino: %d).\n", file->name, file->ino); + + // Remove the file from the list of opened files. + list_head_remove(&file->siblings); + pr_debug("null_close: Removed file `%s` from the opened file list.\n", file->name); + + // Free the file from cache. + kmem_cache_free(file); + pr_debug("null_close: Freed memory for file `%s`.\n", file->name); } return 0; } + /// @brief Writes data to a null device file. /// /// @param file A pointer to the vfs_file_t structure representing the file to write to. diff --git a/mentos/src/fs/attr.c b/mentos/src/fs/attr.c index 9db3b78c..6a26a9cd 100644 --- a/mentos/src/fs/attr.c +++ b/mentos/src/fs/attr.c @@ -13,7 +13,7 @@ #include "process/process.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/printk.h" #include "system/syscall.h" diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index f9378db3..c98cbf89 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -22,7 +22,7 @@ #include "process/scheduler.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/stat.h" #define EXT2_SUPERBLOCK_MAGIC 0xEF53 ///< Magic value used to identify an ext2 filesystem. @@ -368,7 +368,7 @@ static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nb static ssize_t ext2_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte); static off_t ext2_lseek(vfs_file_t *file, off_t offset, int whence); static int ext2_fstat(vfs_file_t *file, stat_t *stat); -static int ext2_ioctl(vfs_file_t *file, int request, void *data); +static long ext2_ioctl(vfs_file_t *file, unsigned int request, unsigned long data); static ssize_t ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count); static ssize_t ext2_readlink(const char *path, char *buffer, size_t bufsize); static int ext2_fsetattr(vfs_file_t *file, struct iattr *attr); @@ -597,7 +597,7 @@ static void ext2_dump_bgdt(ext2_filesystem_t *fs) // Allocate the cache. uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL); // Clean the cache. - memset(cache, 0, fs->ext2_buffer_cache->size); + memset(cache, 0, fs->block_size); for (uint32_t i = 0; i < fs->block_groups_count; ++i) { // Get the pointer to the current group descriptor. ext2_group_descriptor_t *gd = &(fs->block_groups[i]); @@ -663,12 +663,23 @@ static void ext2_dump_filesystem(ext2_filesystem_t *fs) /// @return a pointer to the cache. static inline uint8_t *ext2_alloc_cache(ext2_filesystem_t *fs) { + // Validate input. + if (!fs) { + pr_err("Invalid input: filesystem pointer is NULL."); + return NULL; + } + // Allocate the cache. uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL); + if (!cache) { + // Log critical error if cache allocation fails. + pr_crit("Failed to allocate cache for EXT2 operations."); + return NULL; + } + // Clean the cache. - memset(cache, 0, fs->ext2_buffer_cache->size); - // Check the cache. - assert(cache && "Failed to allocate cache for EXT2 operations."); + memset(cache, 0, fs->block_size); + return cache; } @@ -676,12 +687,14 @@ static inline uint8_t *ext2_alloc_cache(ext2_filesystem_t *fs) /// @param cache pointer to the cache. static inline void ext2_dealloc_cache(uint8_t *cache) { - // Check the cache. - assert(cache && "Received invalid EXT2 cache."); + // Validate the input. + if (!cache) { + pr_err("Invalid input: cache pointer is NULL or already freed."); + return; + } + // Free the cache. kmem_cache_free(cache); - // Clean pointer. - *cache = 0; } /// @brief Returns the rec_len from the given name. @@ -689,6 +702,13 @@ static inline void ext2_dealloc_cache(uint8_t *cache) /// @return the rec_len value. static inline uint32_t ext2_get_rec_len_from_name(const char *name) { + // Validate input. + if (!name) { + pr_err("Invalid input: name is NULL."); + return 0; + } + + // Compute and return the record length. return round_up(sizeof(ext2_dirent_t) + strlen(name) + 1, 4); } @@ -1193,7 +1213,7 @@ static uint32_t ext2_allocate_block(ext2_filesystem_t *fs) pr_warning("Failed to write superblock.\n"); } // Empty out the new block content. - memset(cache, 0, fs->ext2_buffer_cache->size); + memset(cache, 0, fs->block_size); // Write the empty content of the new block. if (ext2_write_block(fs, block_index, cache) < 0) { pr_err("We failed to clean the content of the newly allocated block.\n"); @@ -1860,7 +1880,7 @@ static int ext2_clean_inode_content(ext2_filesystem_t *fs, ext2_inode_t *inode, // Allocate the cache. uint8_t *cache = ext2_alloc_cache(fs); // Get the cache size. - size_t cache_size = fs->ext2_buffer_cache->size; + size_t cache_size = fs->block_size; // int ret = 0; for (ssize_t offset = 0, to_write; offset < inode->size;) { @@ -2971,21 +2991,40 @@ static int ext2_unlink(const char *path) /// @return 0 on success, -errno on failure. static int ext2_close(vfs_file_t *file) { - // Get the filesystem. + // Validate the file pointer. + if (file == NULL) { + pr_err("ext2_close: Invalid file pointer (NULL).\n"); + return -EINVAL; // Invalid argument error + } + + // Get the filesystem from the device. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { - pr_err("The file does not belong to an EXT2 filesystem `%s`.\n", file->name); - return -1; + pr_err("ext2_close: File `%s` does not belong to a valid EXT2 filesystem.\n", file->name); + return -EINVAL; } - // We cannot close the root. + + // Ensure we are not trying to close the root. if (file == fs->root) { - return -1; + pr_warning("ext2_close: Attempted to close the root file `%s`.\n", file->name); + return -EPERM; } - pr_debug("ext2_close(ino: %d, file: \"%s\")\n", file->ino, file->name); - // Remove the file from the list of opened files. - list_head_remove(&file->siblings); - // Free the cache. - kmem_cache_free(file); + + pr_debug("ext2_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count - 1); + + // Decrement the reference count for the file and close if last reference. + if (--file->count == 0) { + pr_debug("ext2_close: Closing file `%s` (ino: %d).\n", file->name, file->ino); + + // Remove the file from the list of opened files. + list_head_remove(&file->siblings); + pr_debug("ext2_close: Removed file `%s` from the opened file list.\n", file->name); + + // Free the file from cache. + pr_debug("ext2_close: Freeing memory for file `%s`.\n", file->name); + kmem_cache_free(file); + } + return 0; } @@ -3102,18 +3141,26 @@ static off_t ext2_lseek(vfs_file_t *file, off_t offset, int whence) } /// @brief Saves the information concerning the file. -/// @param inode The inode containing the data. -/// @param stat The structure where the information are stored. -/// @return 0 if success. -static int __ext2_stat(ext2_inode_t *inode, stat_t *stat) -{ - stat->st_mode = inode->mode; - stat->st_uid = inode->uid; - stat->st_gid = inode->gid; - stat->st_size = inode->size; - stat->st_atime = inode->atime; - stat->st_mtime = inode->mtime; - stat->st_ctime = inode->ctime; +/// @param fs The ext2 filesystem containing the file. +/// @param inode The inode containing the file data. +/// @param inode_index The index of the inode in the filesystem. +/// @param stat The structure where the information is stored. +/// @return 0 on success, or a negative error code on failure. +static int __ext2_stat(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t inode_index, stat_t *stat) +{ + stat->st_dev = fs->block_device->ino; + stat->st_ino = inode_index; + stat->st_mode = inode->mode; + stat->st_nlink = inode->links_count; + stat->st_uid = inode->uid; + stat->st_gid = inode->gid; + stat->st_rdev = 0; + stat->st_size = inode->size; + stat->st_blksize = fs->block_size; + stat->st_blocks = inode->blocks_count; + stat->st_atime = inode->atime; + stat->st_mtime = inode->mtime; + stat->st_ctime = inode->ctime; return 0; } @@ -3144,12 +3191,8 @@ static int ext2_fstat(vfs_file_t *file, stat_t *stat) pr_err("Failed to read the inode `%s`.\n", file->name); return -ENOENT; } - /// ID of device containing file. - stat->st_dev = fs->block_device->ino; - // Set the inode. - stat->st_ino = file->ino; // Set the rest of the structure. - return __ext2_stat(&inode, stat); + return __ext2_stat(fs, &inode, file->ino, stat); } /// @brief Retrieves information concerning the file at the given position. @@ -3178,12 +3221,8 @@ static int ext2_stat(const char *path, stat_t *stat) pr_err("ext2_stat(path: %s): Failed to read the inode of `%s`.\n", path, search.direntry.name); return -ENOENT; } - /// ID of device containing file. - stat->st_dev = fs->block_device->ino; - // Set the inode. - stat->st_ino = search.direntry.inode; // Set the rest of the structure. - return __ext2_stat(&inode, stat); + return __ext2_stat(fs, &inode, search.direntry.inode, stat); } /// @brief Perform the I/O control operation specified by REQUEST on FD. One @@ -3192,7 +3231,7 @@ static int ext2_stat(const char *path, stat_t *stat) /// @param request the device-dependent request code /// @param data an untyped pointer to memory. /// @return Return value depends on REQUEST. Usually -1 indicates error. -static int ext2_ioctl(vfs_file_t *file, int request, void *data) +static long ext2_ioctl(vfs_file_t *file, unsigned int request, unsigned long data) { return -1; } @@ -3566,6 +3605,17 @@ static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path) GFP_KERNEL, NULL, NULL); + + // uint8_t *caches[100]; + // for (size_t i = 0; i < 100; ++i) { + // caches[i] = ext2_alloc_cache(fs); + // } + // for (size_t i = 0; i < 100; ++i) { + // kmem_cache_free(caches[i]); + // } + + // while (1) {} + // Compute the maximum number of inodes per block. fs->inodes_per_block_count = fs->block_size / fs->superblock.inode_size; // Compute the number of blocks per block. This value is mostly used for @@ -3684,7 +3734,8 @@ static vfs_file_t *ext2_mount_callback(const char *path, const char *device) { super_block_t *sb = vfs_get_superblock(device); if (sb == NULL) { - pr_err("mount_callback(%s, %s): Cannot find the superblock!\n", path, device); + pr_err("mount_callback(%s, %s): Cannot find the superblock (%s)!\n", path, device, device); + vfs_dump_superblocks(LOGLEVEL_ERR); return NULL; } vfs_file_t *block_device = sb->root; @@ -3700,7 +3751,7 @@ static vfs_file_t *ext2_mount_callback(const char *path, const char *device) } /// Filesystem information. -static file_system_type ext2_file_system_type = { +static file_system_type_t ext2_file_system_type = { .name = "ext2", .fs_flags = 0, .mount = ext2_mount_callback diff --git a/mentos/src/fs/fcntl.c b/mentos/src/fs/fcntl.c new file mode 100644 index 00000000..a5a5de1f --- /dev/null +++ b/mentos/src/fs/fcntl.c @@ -0,0 +1,32 @@ +/// @file fcntl.c +/// @brief +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "process/scheduler.h" +#include "system/syscall.h" +#include "errno.h" +#include "fs/vfs.h" + +long sys_fcntl(int fd, unsigned int request, unsigned long data) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Verify that the file exists. + vfs_file_t *file = vfd->file_struct; + if (file == NULL) { + return -ENOSYS; + } + + // Perform the ioctl operation. + return vfs_fcntl(file, request, data); +} diff --git a/mentos/src/fs/ioctl.c b/mentos/src/fs/ioctl.c index ae562779..dc03c8a7 100644 --- a/mentos/src/fs/ioctl.c +++ b/mentos/src/fs/ioctl.c @@ -3,14 +3,13 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fs/ioctl.h" #include "fs/vfs.h" #include "process/scheduler.h" #include "stdio.h" -#include "sys/errno.h" +#include "errno.h" #include "system/printk.h" -int sys_ioctl(int fd, int request, void *data) +long sys_ioctl(int fd, unsigned int request, unsigned long data) { // Get the current task. task_struct *task = scheduler_get_current_process(); @@ -23,12 +22,12 @@ int sys_ioctl(int fd, int request, void *data) // Get the file descriptor. vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - // Get the file. + // Verify that the file exists. vfs_file_t *file = vfd->file_struct; if (file == NULL) { return -ENOSYS; } - // Perform the ioctl. + // Perform the ioctl operation. return vfs_ioctl(file, request, data); } diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c index 9b37f74b..97c37d8a 100644 --- a/mentos/src/fs/namei.c +++ b/mentos/src/fs/namei.c @@ -16,7 +16,7 @@ #include "sys/stat.h" #include "fs/vfs.h" #include "process/scheduler.h" -#include "sys/errno.h" +#include "errno.h" #include "strerror.h" #include "string.h" diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c index afa1cb1c..a9a16a2e 100644 --- a/mentos/src/fs/open.c +++ b/mentos/src/fs/open.c @@ -11,7 +11,7 @@ #include "process/process.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/printk.h" #include "system/syscall.h" diff --git a/mentos/src/fs/pipe.c b/mentos/src/fs/pipe.c new file mode 100644 index 00000000..6228ba77 --- /dev/null +++ b/mentos/src/fs/pipe.c @@ -0,0 +1,1227 @@ +/// @file pipe.c +/// @brief PIPE functions and structures implementation. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. +/// @details +/// This haiku is to celebrate the functioning pipes: +/// Data flows like streams, +/// Silent pipes breathe in and out, +/// Code sings clear and true. + +// ============================================================================ +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[PIPE ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. +// ============================================================================ + +#include "fs/pipe.h" + +#include "assert.h" +#include "mem/kheap.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "errno.h" +#include "strerror.h" +#include "list_head.h" +#include "fs/vfs.h" +#include "sys/stat.h" +#include "fcntl.h" +#include "time.h" +#include "system/syscall.h" + +// ============================================================================ +// Virtual FileSystem (VFS) Operaions +// ============================================================================ + +// static int pipe_buffer_confirm(pipe_inode_info_t *, size_t); +// static int pipe_buffer_empty(pipe_inode_info_t *pipe_info, size_t index); +// static size_t pipe_buffer_available(pipe_inode_info_t *pipe_info, size_t index); +// static size_t pipe_buffer_capacity(pipe_inode_info_t *pipe_info, size_t index); +// static ssize_t pipe_buffer_read(pipe_inode_info_t *pipe_info, size_t index, char *dest, size_t count); +// static int pipe_buffer_write(pipe_inode_info_t *pipe_info, size_t index, const char *src, size_t count); + +static int pipe_stat(const char *path, stat_t *stat); + +static vfs_file_t *pipe_open(const char *path, int flags, mode_t mode); +static int pipe_unlink(const char *path); +static int pipe_close(vfs_file_t *file); +static ssize_t pipe_read(vfs_file_t *file, char *buffer, off_t offset, size_t nbyte); +static ssize_t pipe_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte); +static off_t pipe_lseek(vfs_file_t *file, off_t offset, int whence); +static int pipe_fstat(vfs_file_t *file, stat_t *stat); +static long pipe_fcntl(vfs_file_t *file, unsigned int request, unsigned long data); + +/// @brief Operations for managing pipe buffers in the kernel. +static struct pipe_buf_operations anonymous_pipe_ops = { + .confirm = NULL, // pipe_buffer_confirm, + .empty = NULL, // pipe_buffer_empty, + .available = NULL, // pipe_buffer_available, + .capacity = NULL, // pipe_buffer_capacity, + .read = NULL, // pipe_buffer_read, + .write = NULL, // pipe_buffer_write, +}; + +/// @brief Operations for managing pipe buffers in the kernel. +static struct pipe_buf_operations named_pipe_ops = { + .confirm = NULL, //pipe_buffer_confirm, + .empty = NULL, //pipe_buffer_empty, + .available = NULL, //pipe_buffer_available, + .capacity = NULL, //pipe_buffer_capacity, + .read = NULL, //pipe_buffer_read, + .write = NULL, //pipe_buffer_write, +}; + +/// @brief Filesystem-level operations for managing pipes. +static vfs_sys_operations_t pipe_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = pipe_stat, + .creat_f = NULL, + .symlink_f = NULL, +}; + +/// @brief File operations for pipe handling in the filesystem. +static vfs_file_operations_t pipe_fs_operations = { + .open_f = pipe_open, + .unlink_f = pipe_unlink, + .close_f = pipe_close, + .read_f = pipe_read, + .write_f = pipe_write, + .lseek_f = pipe_lseek, + .stat_f = pipe_fstat, + .ioctl_f = NULL, + .fcntl_f = pipe_fcntl, + .getdents_f = NULL, + .readlink_f = NULL, +}; + +// static list_head named_pipes; + +// ============================================================================ +// MEMORY MANAGEMENT (Private) +// ============================================================================ + +/// @brief Initializes a pipe buffer. +/// @param pipe_buffer Pointer to the `pipe_buffer_t` structure to initialize. +/// @param ops Pointer to the `pipe_buf_operations` structure defining buffer operations. +/// @return 0 on success, -ENOMEM if page allocation fails. +static inline int __pipe_buffer_init(pipe_buffer_t *pipe_buffer, struct pipe_buf_operations *ops) +{ + // Check if we received a valid pipe buffer. + assert(pipe_buffer && "Received a null pipe buffer."); + + // Initialize the pipe buffer to zero. + memset(pipe_buffer, 0, sizeof(pipe_buffer_t)); + + // Initialize additional fields in the pipe_buffer_t structure. + pipe_buffer->len = 0; + pipe_buffer->offset = 0; + pipe_buffer->ops = ops; + + return 0; +} + +/// @brief De-initializes a pipe buffer. +/// @param pipe_buffer Pointer to the `pipe_buffer_t` structure to deinitialize. +/// This should not be NULL. +static inline void __pipe_buffer_deinit(pipe_buffer_t *pipe_buffer) +{ + // Check if we received a valid pipe buffer. + assert(pipe_buffer && "Received a null pipe buffer."); + + // Reset other fields for safety. + pipe_buffer->len = 0; + pipe_buffer->offset = 0; + pipe_buffer->ops = NULL; +} + +/// @brief Allocates and initializes a new `pipe_inode_info_t` structure. +/// @param ops Pointer to the `pipe_buf_operations` structure for buffer operations. +/// @return Pointer to the allocated and initialized `pipe_inode_info_t` +/// structure, or NULL if allocation fails. +static inline pipe_inode_info_t *__pipe_inode_info_alloc(struct pipe_buf_operations *ops) +{ + // Allocate memory for the pipe_inode_info_t structure. + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)kmalloc(sizeof(pipe_inode_info_t)); + if (!pipe_info) { + pr_err("Failed to allocate memory for pipe_inode_info_t.\n"); + return NULL; + } + + // Zero initialize the allocated memory. + memset(pipe_info, 0, sizeof(pipe_inode_info_t)); + + // Initialize the wait queues. + wait_queue_head_init(&pipe_info->read_wait); + wait_queue_head_init(&pipe_info->write_wait); + + // Initialize the mutex. + mutex_unlock(&pipe_info->mutex); + + // Set the number of buffers. + pipe_info->numbuf = PIPE_NUM_BUFFERS; + + // Initialize each buffer in the buffer array. + for (unsigned int i = 0; i < pipe_info->numbuf; ++i) { + if (__pipe_buffer_init(&pipe_info->bufs[i], ops)) { + pr_err("Failed to initialize pipe buffer %u.\n", i); + + // Deinitialize and free previously initialized buffers + while (i > 0) { + __pipe_buffer_deinit(&pipe_info->bufs[--i]); + } + kfree(pipe_info); + return NULL; + } + } + + // Initialize remaining fields. + pipe_info->read_index = 0; + pipe_info->write_index = 0; + pipe_info->readers = 0; + pipe_info->writers = 0; + + return pipe_info; +} + +/// @brief Deallocates the memory used by a `pipe_inode_info_t` structure. +/// @details Frees memory associated with a `pipe_inode_info_t` structure, +/// including each pipe buffer in the buffer array and the structure itself. +/// @param pipe_info Pointer to the `pipe_inode_info_t` structure to be +/// deallocated. This should not be NULL. +static inline void __pipe_inode_info_dealloc(pipe_inode_info_t *pipe_info) +{ + // Ensure that the provided pointer is valid. + assert(pipe_info && "Received a NULL pointer."); + + // Free each buffer in the array. + for (unsigned int i = 0; i < pipe_info->numbuf; ++i) { + __pipe_buffer_deinit(&pipe_info->bufs[i]); + } + + // Free the memory used by the pipe_inode_info_t structure itself. + kfree(pipe_info); +} + +// ============================================================================ +// PIPE INFO AND BUFFER OPERATIONS (Private) +// ============================================================================ + +/// @brief Converts a linear index to the corresponding buffer index within the buffer limit. +/// @param index The linear index. +/// @return The buffer index within [0, PIPE_NUM_BUFFERS - 1]. +static inline size_t pipe_linear_to_buffer_index(size_t index) +{ + return (index / PIPE_BUFFER_SIZE) % PIPE_NUM_BUFFERS; +} + +/// @brief Calculates the offset within the specified buffer from a linear index. +/// @param index The linear index. +/// @return The offset within the buffer. +static inline size_t pipe_linear_to_offset(size_t index) +{ + return index % PIPE_BUFFER_SIZE; +} + +/// @brief Checks if the specified pipe has any data available in its buffers. +/// @param pipe_info Pointer to the pipe information structure. +/// @return 1 if data is available, 0 if all buffers are empty, -EINVAL on error. +static inline int pipe_info_has_data(pipe_inode_info_t *pipe_info) +{ + // Validate input parameter. + if (!pipe_info) { + pr_err("pipe_info is NULL.\n"); + return -EINVAL; + } + + // Check if data is available in at least one buffer. + for (unsigned int i = 0; i < pipe_info->numbuf; i++) { + if (pipe_info->bufs[i].len > 0) { + return 1; + } + } + + // No data available in any buffer. + return 0; +} + +/// @brief Checks if the specified pipe has available space in any of its buffers. +/// @param pipe_info Pointer to the pipe information structure. +/// @return 1 if space is available, 0 if all buffers are full, -EINVAL on error. +static inline int pipe_info_has_space(pipe_inode_info_t *pipe_info) +{ + // Validate input parameter. + if (!pipe_info) { + pr_err("pipe_info is NULL.\n"); + return -EINVAL; + } + + // Check if at least one buffer has available space. + for (unsigned int i = 0; i < pipe_info->numbuf; i++) { + if (pipe_info->bufs[i].len < PIPE_BUFFER_SIZE) { + return 1; + } + } + + // No space available in any buffer. + return 0; +} + +/// @brief Checks if the specified pipe buffer is empty. +/// @param pipe_buffer Pointer to the pipe buffer structure to check. +/// @return 1 if the buffer is empty (length is 0), or 0 if not or if pipe_buffer is NULL. +static int pipe_buffer_empty(pipe_buffer_t *pipe_buffer) +{ + // Validate input parameter. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return 0; // Return 0 as there's no buffer to check. + } + + // Buffer is empty if len is 0. + return pipe_buffer->len == 0; +} + +/// @brief Retrieves the number of bytes available (unread) in the specified pipe buffer. +/// @param pipe_buffer Pointer to the pipe buffer structure to check. +/// @return The number of bytes available in the buffer, or 0 if pipe_buffer is NULL. +static size_t pipe_buffer_available(pipe_buffer_t *pipe_buffer) +{ + // Validate input parameter. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return 0; // Return 0 as there’s no buffer to check. + } + + // Return the current length of the buffer, representing unread data. + return pipe_buffer->len; +} + +/// @brief Determines the remaining capacity for writing in the specified pipe buffer. +/// @param pipe_buffer Pointer to the pipe buffer structure to check. +/// @return The remaining write capacity in bytes, or 0 if pipe_buffer is NULL. +static inline size_t pipe_buffer_capacity(pipe_buffer_t *pipe_buffer) +{ + // Validate input parameter. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return 0; // Return 0 as there's no buffer to check. + } + + // Calculate available capacity by subtracting the offset + length from the total buffer size. + return PIPE_BUFFER_SIZE - (pipe_buffer->offset + pipe_buffer->len); +} + +/// @brief Determines the number of bytes that can be read from the pipe buffer. +/// @param pipe_buffer Pointer to the pipe buffer structure. +/// @param count The requested number of bytes to read. +/// @return The number of bytes that can be read on success, or a negative errno-style error code on failure. +static ssize_t pipe_calculate_bytes_to_read(pipe_buffer_t *pipe_buffer, size_t count) +{ + // Validate input parameters. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return -EINVAL; + } + if (count == 0) { + pr_err("Invalid read request of 0 bytes (must be positive).\n"); + return -EINVAL; + } + + // Check if there is data available in the buffer. + if (pipe_buffer_empty(pipe_buffer)) { + pr_debug("No data available in buffer.\n"); + return -EAGAIN; + } + + // Return the number of bytes to read based on the requested count and available data. + return (count < pipe_buffer->len) ? count : pipe_buffer->len; +} + +/// @brief Determines the number of bytes that can be safely written to the pipe buffer. +/// @param pipe_buffer Pointer to the specific pipe buffer. +/// @param count Number of bytes the caller wants to write. +/// @return The number of bytes that can be written on success, or a negative error code on failure. +static ssize_t pipe_calculate_bytes_to_write(pipe_buffer_t *pipe_buffer, size_t count) +{ + // Validate input parameters. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return -EINVAL; + } + if (count == 0) { + pr_err("Invalid write request of %u bytes (must be positive).\n", count); + return -EINVAL; + } + + // Calculate available space in the buffer from the current write position. + size_t capacity = pipe_buffer_capacity(pipe_buffer); + if (capacity == 0) { + pr_debug("No space available in buffer for writing.\n"); + return -EAGAIN; + } + + // Return the smaller of the requested write bytes or available space. + return (count < capacity) ? count : capacity; +} + +/// @brief Ensures that the pipe buffer is valid and ready to use. +/// @param pipe_buffer Pointer to the `pipe_buffer_t` structure to confirm. +/// @return 0 if the buffer is valid, or a non-zero error code if it is not. +static int pipe_buffer_confirm(pipe_buffer_t *pipe_buffer) +{ + // Validate input parameters. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return -EINVAL; + } + + // Ensure length and offset are within valid bounds. + if ((pipe_buffer->len + pipe_buffer->offset) > PIPE_BUFFER_SIZE) { + pr_err("Buffer length and offset exceed bounds: len = %u, offset = %u, PIPE_BUFFER_SIZE = %lu.\n", + pipe_buffer->len, pipe_buffer->offset, PIPE_BUFFER_SIZE); + return -EOVERFLOW; + } + + // Ensure operations pointer is valid. + if (!pipe_buffer->ops) { + pr_err("Buffer operations pointer is NULL.\n"); + return -ENXIO; + } + + return 0; +} + +/// @brief Reads data from the specified pipe buffer into the provided buffer. +/// @param pipe_buffer Pointer to the `pipe_buffer_t` structure to read from. +/// @param dest Pointer to the destination buffer where data will be copied. +/// @param count Number of bytes to read from the pipe buffer. +/// @return Number of bytes read on success, or a negative error code on failure. +static ssize_t pipe_buffer_read(pipe_buffer_t *pipe_buffer, char *dest, size_t count) +{ + // Validate input parameters. + if (!pipe_buffer) { + pr_err("pipe_buffer is NULL.\n"); + return -EINVAL; + } + if (!dest) { + pr_err("Destination buffer is NULL.\n"); + return -EINVAL; + } + + ssize_t bytes_to_read = pipe_calculate_bytes_to_read(pipe_buffer, count); + if (bytes_to_read < 0) { + pr_debug("Failed to calculate bytes to read (error[%2d]: %s).\n", -bytes_to_read, strerror(-bytes_to_read)); + return bytes_to_read; + } + + // Copy data from the pipe buffer's data at the specified offset. + memcpy(dest, pipe_buffer->data + pipe_buffer->offset, bytes_to_read); + + // Adjust buffer's offset and length to reflect the data consumption. + pipe_buffer->offset += bytes_to_read; + pipe_buffer->len -= bytes_to_read; + + // If all data has been read, reset offset to 0 for reusability. + if (pipe_buffer->len == 0) { + pipe_buffer->offset = 0; + } + + pr_debug("Read %3ld bytes from buffer (offset: %3u, length: %3u).\n", + bytes_to_read, pipe_buffer->offset, pipe_buffer->len); + + return bytes_to_read; +} + +/// @brief Writes data to the specified pipe buffer. +/// @param pipe_buffer Pointer to the pipe buffer structure where data will be written. +/// @param src Pointer to the source buffer containing data to write. +/// @param count The maximum number of bytes to write. +/// @return the number of written bytes on success, or a negative errno-style error code on failure. +static ssize_t pipe_buffer_write(pipe_buffer_t *pipe_buffer, const char *src, size_t count) +{ + // Validate input parameters. + if (!pipe_buffer) { + pr_err("pipe_buffer_write: pipe_buffer is NULL.\n"); + return -EINVAL; + } + if (!src) { + pr_err("pipe_buffer_write: Source buffer is NULL.\n"); + return -EINVAL; + } + + // Determine the actual number of bytes we can write into the buffer. + ssize_t bytes_to_write = pipe_calculate_bytes_to_write(pipe_buffer, count); + if (bytes_to_write < 0) { + pr_debug("pipe_buffer_write: Failed to calculate bytes to write (error[%2d]: %s).\n", -bytes_to_write, strerror(-bytes_to_write)); + return bytes_to_write; + } + + // Write data to the buffer's current write position (offset + length). + memcpy(pipe_buffer->data + pipe_buffer->offset + pipe_buffer->len, src, bytes_to_write); + + // Update the buffer's length to reflect the newly added data. + pipe_buffer->len += bytes_to_write; + + pr_debug("pipe_buffer_write: Wrote %3ld bytes to buffer (offset: %3u, length: %3u).\n", + bytes_to_write, pipe_buffer->offset, pipe_buffer->len); + + return bytes_to_write; +} + +// ============================================================================ +// Wait Queue Functions +// ============================================================================ + +/// @brief Wake-up function for processes waiting to read from a pipe. +/// @param wait Pointer to the wait queue entry representing the sleeping process. +/// @param mode The mode to set the task to upon wake-up. +/// @param sync Synchronization flag (not used in this implementation). +/// @return 1 if the process is woken up, 0 if it remains in the wait queue. +int pipe_read_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) +{ + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)wait->private; + // Validate that data is available in the pipe for reading. + if ((pipe_info_has_data(pipe_info) > 0) || (pipe_info->writers == 0)) { + // Check if the task is in an appropriate sleep state to be woken up. + if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { + // Set the task's state to the specified wake-up mode. + wait->task->state = mode; + + // Signal that the task has been woken up. + pr_debug("pipe_read_wake_function: Data available or no more writers, waking up reader %d.\n", wait->task->pid); + return 1; + } else { + pr_debug("pipe_read_wake_function: Reader %d not in the correct state for wake-up.\n", wait->task->pid); + } + } else { + pr_debug("pipe_read_wake_function: No data available, reader %d should continue waiting.\n", wait->task->pid); + } + + // No wake-up action taken, continue waiting. + return 0; +} + +/// @brief Wake-up function for processes waiting to write to a pipe. +/// @param wait Pointer to the wait queue entry representing the sleeping process. +/// @param mode The mode to set the task to upon wake-up. +/// @param sync Synchronization flag (not used in this implementation). +/// @return 1 if the process is woken up, 0 if it remains in the wait queue. +int pipe_write_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) +{ + // Check if there is available space in the pipe for writing. + if (pipe_info_has_space((pipe_inode_info_t *)wait->private) > 0) { + // Only tasks in the state TASK_UNINTERRUPTIBLE or TASK_STOPPED can be woken up. + if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) { + // Set the wake-up mode for the task. + wait->task->state = mode; + + // Signal that the task has been woken up. + pr_debug("pipe_write_wake_function: Space available, waking up writer %d.\n", wait->task->pid); + return 1; + } else { + pr_debug("pipe_write_wake_function: Writer %d not in the correct state for wake-up.\n", wait->task->pid); + } + } else { + pr_debug("pipe_write_wake_function: No space available, writer %d should continue waiting.\n", wait->task->pid); + } + + // No wake-up action taken, continue waiting. + return 0; +} + +/// @brief Wakes up tasks in the specified wait queue if their wake-up condition is met. +/// @param wait_queue Pointer to the wait queue from which tasks should be woken up. +/// @param debug_msg Debug message describing the wake-up context. +static void pipe_wake_up_tasks(wait_queue_head_t *wait_queue, const char *debug_msg) +{ + // Validate input parameters. + if (!wait_queue) { + pr_err("pipe_wake_up_tasks: wait_queue is NULL.\n"); + return; + } + + list_for_each_safe_decl(it, store, &wait_queue->task_list) + { + wait_queue_entry_t *wait_queue_entry = list_entry(it, wait_queue_entry_t, task_list); + + // Run the wakeup test function for the waiting task. + if (wait_queue_entry->func(wait_queue_entry, TASK_RUNNING, 0)) { + // Task is ready, remove from the wait queue. + remove_wait_queue(wait_queue, wait_queue_entry); + + // Log and free the memory associated with the wait entry. + pr_debug("%s: Process %d woken up.\n", debug_msg, wait_queue_entry->task->pid); + wait_queue_entry_dealloc(wait_queue_entry); + } + } +} + +/// @brief Puts the current process to sleep on the specified wait queue if blocking is needed. +/// @param pipe_info Pointer to the pipe information structure. +/// @param wait_queue Pointer to the wait queue on which to put the process to sleep. +/// @param wake_function Wake-up function associated with the wait queue entry. +/// @param debug_msg Debug message describing the block context. +/// @return 0 after scheduling the blocking behavior. +static int pipe_put_process_to_sleep( + pipe_inode_info_t *pipe_info, + wait_queue_head_t *wait_queue, + int (*wake_function)(wait_queue_entry_t *, unsigned, int), + const char *debug_msg) +{ + // Blocking behavior: Put the process to sleep until the condition is met. + wait_queue_entry_t *wait_queue_entry = sleep_on(wait_queue); + assert(wait_queue_entry && "Failed to allocate wait_queue_entry_t."); + + // Set the wake-up function and private data for the wait entry. + wait_queue_entry->func = wake_function; + wait_queue_entry->private = pipe_info; + + // Indicate blocking behavior was scheduled. + return 0; +} + +// ============================================================================ +// Virtual FileSystem (VFS) Functions +// ============================================================================ + +/// @brief Checks if the pipe is in blocking mode. +/// @param file Pointer to the vfs_file_t structure representing the pipe file. +/// @return 1 if the pipe is in blocking mode, 0 if it is in non-blocking mode. +static inline int pipe_is_blocking(vfs_file_t *file) +{ + // Ensure the file pointer is valid before dereferencing. + if (!file) { + pr_err("pipe_check_blocking: file pointer is NULL.\n"); + return 0; // Assume non-blocking mode if file is NULL. + } + + // Check if the O_NONBLOCK flag is NOT set in the file's flags. + // If NOT set, the pipe operates in blocking mode and the function returns 1. + // If set, the pipe is in non-blocking mode, and the function returns 0. + return (file->flags & O_NONBLOCK) == 0; +} + +/// @brief Creates a VFS file structure for a pipe. +/// @param path Path to the file (not used here but may be needed elsewhere). +/// @param flags Open flags (e.g., O_RDONLY, O_WRONLY). +/// @param mode File permissions (e.g., 0666 for read/write for all). +/// @return Pointer to the newly created VFS file, or NULL on failure. +static inline vfs_file_t *pipe_create_file_struct(const char *path, int flags, mode_t mode) +{ + // Allocate memory for the VFS file structure. + vfs_file_t *vfs_file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (!vfs_file) { + pr_err("Failed to allocate memory for VFS file!\n"); + return NULL; + } + + // Initialize all fields to zero. + memset(vfs_file, 0, sizeof(vfs_file_t)); + + // Assign the path if necessary. + if (path) { + memcpy(vfs_file->name, path, strlen(path) + 1); + vfs_file->name[strlen(path) + 1] = 0; + } else { + memset(vfs_file->name, 0, sizeof(vfs_file->name)); + } + + // Set the file type to FIFO and apply the flags provided by the caller. + // Only keep relevant access mode bits. + vfs_file->flags = S_IFIFO | (flags & O_ACCMODE); + + // Set the permissions mask with the effective mode. Set only permission + // bits. + vfs_file->mask = mode & 0777; + + // Set operations for system and filesystem functions specific to pipes. + vfs_file->sys_operations = &pipe_sys_operations; + vfs_file->fs_operations = &pipe_fs_operations; + + // Initialize file metadata fields as required. + vfs_file->refcount = 1; // Initial reference count. + vfs_file->f_pos = 0; // Start position for file operations. + + // Initialize the list head for siblings. + list_head_init(&vfs_file->siblings); + + // Optionally, set other metadata fields (timestamps can be set here if needed). + vfs_file->atime = sys_time(NULL); // Set access time. + vfs_file->mtime = sys_time(NULL); // Set modification time. + vfs_file->ctime = sys_time(NULL); // Set change time. + + // Set the count to opne. + vfs_file->count = 1; + + return vfs_file; +} + +/// @brief Retrieves statistics for a pipe file. +/// @param path Path to the pipe file. +/// @param stat Pointer to the stat structure where the statistics will be stored. +/// @return 0 on success, or a negative error code on failure. +static int pipe_stat(const char *path, stat_t *stat) +{ + // Validate the input parameters. + if (!path || !stat) { + return -EINVAL; + } + + // Placeholder: Fetch file info based on the path. + // In a real implementation, retrieve file info, file size, permissions, etc. + memset(stat, 0, sizeof(stat_t)); // Initialize the stat structure. + + // Populate stat information (e.g., file type, permissions, size). + stat->st_mode = S_IFIFO | 0644; // Indicate a FIFO (pipe) with rw-r--r-- permissions. + stat->st_size = 0; // Pipes typically have a size of 0. + stat->st_nlink = 1; // Single link count for pipes. + + return 0; +} + +/// @brief Opens a pipe file with the specified path, flags, and mode. +/// @param path The path of the pipe to open. +/// @param flags File status flags and access modes. +/// @param mode File mode bits to apply when creating a new pipe. +/// @return Pointer to the opened vfs_file_t structure, or NULL on failure. +static vfs_file_t *pipe_open(const char *path, int flags, mode_t mode) +{ + // Validate the path parameter. + if (!path) { + pr_err("Invalid path: path is NULL.\n"); + return NULL; + } + + // Retrieve the current task structure. + task_struct *task = scheduler_get_current_process(); + assert(task && "Failed to retrieve current task."); + + // Iterate through the file descriptors in the task + for (int fd = 0; fd < task->max_fd; fd++) { + // Get the file. + vfs_file_t *file = task->fd_list[fd].file_struct; + // Check if the file descriptor is associated with a pipe. + if (file && S_ISFIFO(file->flags) && (strcmp(file->name, path) == 0)) { + // Check if the requested flags match existing file access mode. + if ((flags & O_ACCMODE) != (file->flags & O_ACCMODE)) { + pr_err("Requested flags do not match existing pipe access mode.\n"); + return NULL; + } + return file; // Return existing pipe file. + } + } + + // Allocate and initialize a new vfs_file_t structure for the pipe. + vfs_file_t *new_file = pipe_create_file_struct(path, flags, mode); + if (!new_file) { + pr_err("Failed to allocate memory for vfs_file_t structure for path: %s.\n", path); + return NULL; + } + + // Allocate and initialize the pipe_inode_info structure. + pipe_inode_info_t *pipe_info = __pipe_inode_info_alloc(&named_pipe_ops); + if (!pipe_info) { + pr_err("Failed to allocate memory for pipe_inode_info structure.\n"); + // Free allocated new_file structure before returning. + kfree(new_file); + return NULL; + } + + // Link the pipe_info to the vfs_file structure. + new_file->device = pipe_info; + + // Confirm each buffer in pipe_info is properly initialized. + pipe_buffer_t *pipe_buffer; + for (size_t buffer_index = 0; buffer_index < pipe_info->numbuf; ++buffer_index) { + pipe_buffer = &pipe_info->bufs[buffer_index]; + + int ret = pipe_buffer_confirm(pipe_buffer); + if (ret < 0) { + pr_err("Buffer confirmation failed for buffer %u (error[%2d]: %s).\n", buffer_index, -ret, strerror(-ret)); + // Free pipe info structure. + __pipe_inode_info_dealloc(pipe_info); + // Free allocated new_file structure before returning. + kfree(new_file); + return NULL; + } + } + + return new_file; +} + +/// @brief Unlinks (closes) an opened pipe file with the specified path. +/// @param path Path of the pipe file to unlink. +/// @return 0 on success, -1 on failure if pipe is not found or other error occurs. +static int pipe_unlink(const char *path) +{ + // Validate the path parameter. + if (!path) { + pr_err("Invalid path - path is NULL.\n"); + return -1; + } + + pr_debug("Attempting to unlink pipe with path: %s\n", path); + + // Retrieve the current task structure. + task_struct *task = scheduler_get_current_process(); + assert(task && "Failed to retrieve current task."); + + // Iterate through the file descriptors in the task + for (int fd = 0; fd < task->max_fd; fd++) { + // Get the file. + vfs_file_t *file = task->fd_list[fd].file_struct; + + // Check if the file name matches the specified path. + if (file && strcmp(file->name, path) == 0) { + pr_debug("Pipe file found for path %s at FD %d.\n", path, fd); + + // Remove from the task's pipe list. + pr_debug("Removing file from task's pipe list.\n"); + list_head_remove(&file->siblings); + + // Free the associated pipe_inode_info and the vfs_file structure. + if (file->device) { + pr_debug("Deallocating pipe_inode_info for file.\n"); + __pipe_inode_info_dealloc((pipe_inode_info_t *)file->device); + } + + // We can free the file now. + pr_debug("Freeing vfs_file structure.\n"); + kfree(file); + + pr_info("Successfully unlinked pipe: %s\n", path); + return 0; + } + } + + // Pipe not found, return an error. + pr_err("Pipe unlink failed - no pipe found with path %s.\n", path); + return -1; +} + +/// @brief Closes the specified pipe file, adjusting reader/writer counts and +/// freeing resources if no references remain. +/// @param file Pointer to the `vfs_file_t` structure representing the pipe file. +/// @return 0 on success, -errno on failure. +static int pipe_close(vfs_file_t *file) +{ + // Validate input parameters. + if (!file) { + pr_err("Invalid argument - file is NULL.\n"); + return -EINVAL; + } + if (!file->device) { + pr_err("Invalid file - file device is NULL.\n"); + return -EINVAL; + } + + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)file->device; + + // Determine if this is a read or write end and decrement the appropriate count. + if ((file->flags & O_ACCMODE) == O_WRONLY) { + if (pipe_info->writers > 0) { + pipe_info->writers--; + pr_debug("Decremented writers (count: %u, readers: %u, writers: %u).\n", + file->count, pipe_info->readers, pipe_info->writers); + } else { + pr_warning("Writers count is already zero.\n"); + } + } else if ((file->flags & O_ACCMODE) == O_RDONLY) { + if (pipe_info->readers > 0) { + pipe_info->readers--; + pr_debug("Decremented readers (count: %u, readers: %u, writers: %u).\n", + file->count, pipe_info->readers, pipe_info->writers); + } else { + pr_warning("Readers count is already zero.\n"); + } + } else { + pr_warning("Unknown pipe file access mode, possibly incorrect flags.\n"); + } + + // If all writers have closed, wake up waiting readers. + if (pipe_info->writers == 0) { + pr_debug("All writers have closed the pipe. Waking up readers.\n"); + pipe_wake_up_tasks(&pipe_info->read_wait, "pipe_close"); + } + + // If both readers and writers are zero, free the pipe resources. + if (--file->count == 0) { + if ((pipe_info->readers == 0) && (pipe_info->writers == 0)) { + pr_debug("Fully closing and deallocating pipe.\n"); + // Deallocate the pipe info. + __pipe_inode_info_dealloc(pipe_info); + } else { + pr_debug("Closing and deallocating just the vfs file.\n"); + } + + // Remove the file from the list of opened files. + list_head_remove(&file->siblings); + + // Free the file from cache. + kmem_cache_free(file); + } + + return 0; +} + +/// @brief Reads data from the specified pipe file into the provided buffer. +/// @param file Pointer to the `vfs_file_t` structure representing the pipe file. +/// @param buffer Buffer where the data will be stored. +/// @param offset Unused for pipes, but included for interface compatibility. +/// @param nbyte Maximum number of bytes to read. +/// @return Number of bytes read on success, 0 if no data available in non-blocking mode, or -1 on error. +static ssize_t pipe_read(vfs_file_t *file, char *buffer, off_t offset, size_t nbyte) +{ + // Validate input parameters. + if (!file) { + pr_err("Invalid argument - file is NULL.\n"); + return -1; + } + if (!buffer) { + pr_err("Invalid argument - buffer is NULL.\n"); + return -1; + } + if (!file->device) { + pr_err("Invalid file - file device is NULL.\n"); + return -1; + } + + // Retrieve the current task structure. + task_struct *task = scheduler_get_current_process(); + assert(task && "Failed to retrieve current task."); + + // Retrieve the pipe information structure. + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)file->device; + + // Acquire the pipe mutex to ensure safe access. + mutex_lock(&pipe_info->mutex, task->pid); + + // Return 0 if there are no writers left. + if (pipe_info->writers == 0) { + pr_debug("No writers left.\n"); + return 0; + } + + ssize_t bytes_read = 0; + + if (pipe_info_has_data(pipe_info)) { + // Loop to read data from the pipe until requested bytes are read or an error occurs. + while (bytes_read < nbyte) { + // Wrap read_index around when exceeding max buffer capacity. + pipe_info->read_index %= (pipe_info->numbuf * PIPE_BUFFER_SIZE); + + // Calculate the buffer index for the current read position. + size_t buffer_index = pipe_linear_to_buffer_index(pipe_info->read_index); + pipe_buffer_t *pipe_buffer = &pipe_info->bufs[buffer_index]; + + // Confirm that the buffer is ready to be read. + if (pipe_buffer_confirm(pipe_buffer) < 0) { + pr_err("Failed to confirm readiness of buffer %u for reading.\n", buffer_index); + break; // Stop if there’s no data to read. + } + + // Calculate bytes to read in this iteration, considering the remaining requested bytes. + ssize_t bytes_to_read = pipe_buffer_read(pipe_buffer, buffer + bytes_read, nbyte - bytes_read); + if (bytes_to_read < 0) { + pr_err("Error reading from pipe buffer (error[%2d]: %s).\n", -bytes_to_read, strerror(-bytes_to_read)); + bytes_read = -bytes_to_read; + break; + } + + // Update the total bytes read and the read index. + bytes_read = bytes_read + bytes_to_read; + pipe_info->read_index = pipe_info->read_index + bytes_to_read; + + // Wake up tasks that might be waiting to write to the pipe. + pipe_wake_up_tasks(&pipe_info->write_wait, "pipe_read"); + } + } else { + // If in blocking mode, put the process to sleep until data is available. + if (pipe_is_blocking(file)) { + pipe_put_process_to_sleep(pipe_info, &pipe_info->read_wait, pipe_read_wake_function, "pipe_read"); + } + // TODO: We currently do not save kernel regs status, so we need a + // work-around when putting processes to sleep. + bytes_read = -EAGAIN; + } + + // Release the mutex after reading. + mutex_unlock(&pipe_info->mutex); + + return bytes_read; +} + +/// @brief Writes data to the specified pipe file from the provided buffer. +/// @param file Pointer to the `vfs_file_t` structure representing the pipe file. +/// @param buffer Buffer containing the data to write. +/// @param offset Unused for pipes, but included for interface compatibility. +/// @param nbyte Maximum number of bytes to write. +/// @return Number of bytes written on success, or -1 on error. +static ssize_t pipe_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte) +{ + // Validate input parameters. + if (!file) { + pr_err("Invalid argument - file is NULL.\n"); + return -1; + } + if (!buffer) { + pr_err("Invalid argument - buffer is NULL.\n"); + return -1; + } + if (!file->device) { + pr_err("Invalid file - file device is NULL.\n"); + return -1; + } + + // Retrieve the current task structure. + task_struct *task = scheduler_get_current_process(); + assert(task && "Failed to retrieve current task."); + + // Retrieve the pipe information structure. + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)file->device; + + // Acquire the pipe mutex to ensure safe access. + mutex_lock(&pipe_info->mutex, task->pid); + + ssize_t bytes_written = 0; + + // Check if there is available space in the pipe for writing. + if (pipe_info_has_space(pipe_info)) { + // Loop to write data to the pipe buffer until the requested number of bytes is written. + while (bytes_written < nbyte) { + // Wrap around write_index when it exceeds the max buffer capacity. + pipe_info->write_index %= (pipe_info->numbuf * PIPE_BUFFER_SIZE); + + // Get the buffer index for the current write position. + size_t buffer_index = pipe_linear_to_buffer_index(pipe_info->write_index); + pipe_buffer_t *pipe_buffer = &pipe_info->bufs[buffer_index]; + + // Confirm the buffer is ready for writing. + if (pipe_buffer_confirm(pipe_buffer) < 0) { + pr_err("Failed to confirm readiness of buffer %u for writing.\n", buffer_index); + bytes_written = -1; + break; + } + + // Attempt to write data into the pipe buffer. + ssize_t bytes_to_write = pipe_buffer_write(pipe_buffer, (const char *)buffer + bytes_written, nbyte - bytes_written); + if (bytes_to_write < 0) { + // Other errors: Log and return immediately. + pr_err("Error writing to pipe buffer (error[%2d]: %s).\n", -bytes_to_write, strerror(-bytes_to_write)); + bytes_written = -1; + break; + } + + // Update the total bytes written and the write index. + bytes_written = bytes_written + bytes_to_write; + pipe_info->write_index = pipe_info->write_index + bytes_to_write; + + // Wake up tasks waiting to read from the pipe. + pipe_wake_up_tasks(&pipe_info->read_wait, "pipe_write"); + } + } else { + // Blocking behavior: Put the process to sleep until space is available. + if (pipe_is_blocking(file)) { + pipe_put_process_to_sleep(pipe_info, &pipe_info->write_wait, pipe_write_wake_function, "pipe_write"); + } + // TODO: We currently do not save kernel regs status, so we need a + // work-around when putting processes to sleep. + bytes_written = -EAGAIN; + } + + // Release the mutex after the write operation is complete. + mutex_unlock(&pipe_info->mutex); + + return bytes_written; +} + +/// @brief Performs a seek operation on a pipe, which is not supported. +/// @param file Pointer to the `vfs_file_t` structure representing the pipe. +/// @param offset The seek offset (unused for pipes). +/// @param whence The seek base (e.g., SEEK_SET, SEEK_CUR, SEEK_END; unused for pipes). +/// @return Always returns -1 as pipes do not support seeking. +static off_t pipe_lseek(vfs_file_t *file, off_t offset, int whence) +{ + return -1; +} + +/// @brief Retrieves file status information for a pipe, which is not supported. +/// @param file Pointer to the `vfs_file_t` structure representing the pipe. +/// @param stat Pointer to the `stat_t` structure to hold file status information. +/// @return Always returns -1 as pipes do not support file status retrieval. +static int pipe_fstat(vfs_file_t *file, stat_t *stat) +{ + return -1; +} + +/// @brief Performs a fcntl operation on a pipe file descriptor +/// @param file Pointer to the vfs_file_t structure representing the pipe file. +/// @param request The fcntl command (e.g., F_GETFL, F_SETFL) +/// @param data Additional argument for setting flags (used with F_SETFL) +/// @return On success, returns 0 for F_SETFL or current flags for F_GETFL; -1 on error with errno set. +static long pipe_fcntl(vfs_file_t *file, unsigned int request, unsigned long data) +{ + if (!file) { + errno = EBADF; + pr_err("Invalid file descriptor.\n"); + return -1; + } + + switch (request) { + case F_GETFL: + pr_debug("Retrieving flags for pipe.\n"); + return file->flags; // Return the current flags for the file descriptor + + case F_SETFL: + pr_debug("Setting flags for pipe.\n"); + // Only handle O_NONBLOCK for simplicity + if (data & O_NONBLOCK) { + file->flags |= O_NONBLOCK; + pr_debug("Set O_NONBLOCK flag.\n"); + } else { + file->flags &= ~O_NONBLOCK; + pr_debug("Cleared O_NONBLOCK flag.\n"); + } + return 0; + + default: + errno = EINVAL; + pr_err("Unsupported request %u.\n", request); + return -1; + } +} + +/// @brief Creates a file descriptor for one end of a pipe. +/// @param pipe_info Pointer to the pipe inode information structure. +/// @param flags Open mode for the pipe (e.g., O_RDONLY or O_WRONLY). +/// @param mode Permission mode for the new pipe file if creating. +/// @return The created file descriptor on success, or -1 on error. +static inline int create_pipe_fd(pipe_inode_info_t *pipe_info, int flags, mode_t mode) +{ + // Validate the pipe_info parameter. + if (!pipe_info) { + pr_err("pipe_info is NULL.\n"); + return -1; + } + + // Retrieve the current task structure. + task_struct *task = scheduler_get_current_process(); + assert(task && "Failed to retrieve current task."); + + // Allocate and initialize a new vfs_file_t structure for the pipe. + vfs_file_t *file = pipe_create_file_struct(NULL, flags, mode); + if (!file) { + pr_err("Failed to allocate memory for vfs_file_t structure.\n"); + return -1; + } + + // Link the pipe_info to the vfs_file structure. + file->device = pipe_info; + + // Get an unused file descriptor. + int fd = get_unused_fd(); + if (fd < 0) { + pr_err("Failed to allocate file descriptor.\n"); + // Close the file if fd allocation fails. + pipe_close(file); + return -1; + } + + // Register the file descriptor in the process's file descriptor table. + task->fd_list[fd].file_struct = file; + task->fd_list[fd].flags_mask = file->flags; + + // Return the created file descriptor. + return fd; +} + +int vfs_update_pipe_counts(task_struct *task, task_struct *old_task) +{ + // Iterate through the file descriptors in the task + for (int fd = 0; fd < task->max_fd; fd++) { + // Get the file. + vfs_file_t *file = task->fd_list[fd].file_struct; + // Check if the file descriptor is associated with a pipe. + if (file && S_ISFIFO(file->flags)) { + // Assume file_struct has a member pipe_info that points to pipe_inode_info_t. + pipe_inode_info_t *pipe_info = (pipe_inode_info_t *)file->device; + + // Increase the readers or writers count. + if (pipe_info) { + // Increment readers or writers count based on the file descriptor's direction. + if ((file->flags & O_ACCMODE) == O_WRONLY) { + // Increment the writers count for the pipe. + ++pipe_info->writers; + pr_debug("Increased writers count for pipe associated with fd %d. New count: %d\n", + fd, pipe_info->writers); + } else if ((file->flags & O_ACCMODE) == O_RDONLY) { + // Increment the readers count for the pipe. + ++pipe_info->readers; + pr_debug("Increased readers count for pipe associated with fd %d. New count: %d\n", + fd, pipe_info->readers); + } else { + pr_warning("Unknown pipe file access mode, possibly incorrect flags.\n"); + } + } + } + } + return 0; +} + +/// @brief System call to create a new pipe. +/// @param fds Array to store read and write file descriptors. +/// @return 0 on success, or -1 on error. +int sys_pipe(int fds[2]) +{ + // Validate input pointer + if (!fds) { + pr_err("Invalid argument: fds is NULL.\n"); + return -1; + } + + // Allocate and initialize the pipe_inode_info structure. + pipe_inode_info_t *pipe_info = __pipe_inode_info_alloc(&anonymous_pipe_ops); + if (!pipe_info) { + pr_err("Failed to allocate memory for pipe_inode_info structure.\n"); + return -1; + } + + // Confirm each buffer in pipe_info is properly initialized. + pipe_buffer_t *pipe_buffer; + for (size_t buffer_index = 0; buffer_index < pipe_info->numbuf; ++buffer_index) { + pipe_buffer = &pipe_info->bufs[buffer_index]; + + int ret = pipe_buffer_confirm(pipe_buffer); + if (ret < 0) { + pr_err("Buffer confirmation failed for buffer %u (error[%2d]: %s).\n", buffer_index, -ret, strerror(-ret)); + // Free pipe info structure. + __pipe_inode_info_dealloc(pipe_info); + // Free allocated file structure before returning. + return -1; + } + } + + // Create file descriptors for reading and writing + int fd_read = create_pipe_fd(pipe_info, O_RDONLY, 0); + int fd_write = create_pipe_fd(pipe_info, O_WRONLY, 0); + if (fd_read < 0 || fd_write < 0) { + pr_err("Failed to create file descriptors.\n"); + if (fd_read >= 0) { sys_close(fd_read); } + if (fd_write >= 0) { sys_close(fd_write); } + __pipe_inode_info_dealloc(pipe_info); + return -1; + } + + pipe_info->readers = 1; + pipe_info->writers = 1; + + // Assign file descriptors to output array + fds[0] = fd_read; + fds[1] = fd_write; + + return 0; // Success +} diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c index a7e28b01..1f4ea71c 100644 --- a/mentos/src/fs/procfs.c +++ b/mentos/src/fs/procfs.c @@ -17,7 +17,7 @@ #include "libgen.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "time.h" /// Maximum length of name in PROCFS. @@ -90,7 +90,7 @@ static ssize_t procfs_read(vfs_file_t *file, char *buffer, off_t offset, size_t static ssize_t procfs_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte); static off_t procfs_lseek(vfs_file_t *file, off_t offset, int whence); static int procfs_fstat(vfs_file_t *file, stat_t *stat); -static int procfs_ioctl(vfs_file_t *file, int request, void *data); +static long procfs_ioctl(vfs_file_t *file, unsigned int request, unsigned long data); static ssize_t procfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count); // ============================================================================ @@ -540,12 +540,27 @@ static vfs_file_t *procfs_open(const char *path, int flags, mode_t mode) /// @return 0 on success, -errno on failure. static int procfs_close(vfs_file_t *file) { - assert(file && "Received null file."); - //pr_debug("procfs_close(%p): VFS file : %p\n", file, file); - // Remove the file from the list of `files` inside its corresponding `procfs_file_t`. - list_head_remove(&file->siblings); - // Free the memory of the file. - kmem_cache_free(file); + // Check if the file pointer is NULL. + if (file == NULL) { + pr_crit("procfs_close: Received NULL file pointer.\n"); + return -EINVAL; + } + + pr_debug("procfs_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count); + + // Decrement the reference count for the file. + if (--file->count == 0) { + pr_debug("procfs_close: Closing file `%s` (ino: %d).\n", file->name, file->ino); + + // Remove the file from the list of opened files. + list_head_remove(&file->siblings); + pr_debug("procfs_close: Removed file `%s` from the opened file list.\n", file->name); + + // Free the file from cache. + kmem_cache_free(file); + pr_debug("procfs_close: Freed memory for file `%s`.\n", file->name); + } + return 0; } @@ -723,7 +738,7 @@ static int procfs_stat(const char *path, stat_t *stat) /// @param request the device-dependent request code /// @param data an untyped pointer to memory. /// @return Return value depends on REQUEST. Usually -1 indicates error. -static int procfs_ioctl(vfs_file_t *file, int request, void *data) +static long procfs_ioctl(vfs_file_t *file, unsigned int request, unsigned long data) { if (file) { procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); @@ -855,7 +870,7 @@ static vfs_file_t *procfs_mount_callback(const char *path, const char *device) } /// Filesystem information. -static file_system_type procfs_file_system_type = { +static file_system_type_t procfs_file_system_type = { .name = "procfs", .fs_flags = 0, .mount = procfs_mount_callback diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index 0a8e68c5..169ba537 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -8,7 +8,7 @@ #include "fs/vfs.h" #include "fs/vfs_types.h" #include "stdio.h" -#include "sys/errno.h" +#include "errno.h" #include "system/panic.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c index 7d6160d1..61b35b6d 100644 --- a/mentos/src/fs/readdir.c +++ b/mentos/src/fs/readdir.c @@ -9,7 +9,7 @@ #include "process/scheduler.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/printk.h" #include "system/syscall.h" diff --git a/mentos/src/fs/stat.c b/mentos/src/fs/stat.c index b65393ac..f59b04a9 100644 --- a/mentos/src/fs/stat.c +++ b/mentos/src/fs/stat.c @@ -9,7 +9,7 @@ #include "mem/kheap.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" int sys_stat(const char *path, stat_t *buf) { diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 2bfaf033..e50c87d7 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -15,7 +15,6 @@ #include "fs/procfs.h" #include "fs/namei.h" #include "fs/vfs.h" -#include "klib/hashmap.h" #include "klib/spinlock.h" #include "libgen.h" #include "process/scheduler.h" @@ -24,13 +23,12 @@ #include "string.h" #include "system/panic.h" #include "system/syscall.h" +#include "fs/pipe.h" -/// The hashmap that associates a type of Filesystem `name` to its `mount` function; -static hashmap_t *vfs_filesystems; /// The list of superblocks. static list_head vfs_super_blocks; -/// The maximum number of filesystem types. -static const unsigned vfs_filesystems_max = 10; +/// The list of filesystems. +static list_head vfs_filesystems; /// Lock for refcount field. static spinlock_t vfs_spinlock_refcount; /// Spinlock for the entire virtual filesystem. @@ -45,72 +43,111 @@ void vfs_init(void) { // Initialize the list of superblocks. list_head_init(&vfs_super_blocks); + // Initialize the list of filesystems. + list_head_init(&vfs_filesystems); // Initialize the caches for superblocks and files. vfs_superblock_cache = KMEM_CREATE(super_block_t); vfs_file_cache = KMEM_CREATE(vfs_file_t); - // Allocate the hashmap for the different filesystems. - vfs_filesystems = hashmap_create( - vfs_filesystems_max, - hashmap_str_hash, - hashmap_str_comp, - hashmap_do_not_duplicate, - hashmap_do_not_free); // Initialize the spinlock. spinlock_init(&vfs_spinlock); spinlock_init(&vfs_spinlock_refcount); } -int vfs_register_filesystem(file_system_type *fs) +/// @brief Finds a filesystem type by its name. +/// @param name The name of the filesystem to find. +/// @return Pointer to the filesystem type if found, or NULL if not found. +file_system_type_t *__vfs_find_filesystem(const char *name) { - if (hashmap_set(vfs_filesystems, fs->name, fs) != NULL) { - pr_err("Filesystem already registered.\n"); - return 0; + list_for_each_decl(it, &vfs_filesystems) + { + file_system_type_t *fs = list_entry(it, file_system_type_t, list); + if (strcmp(fs->name, name) == 0) { + return fs; + } } + return NULL; +} + +int vfs_register_filesystem(file_system_type_t *fs) +{ + assert(__vfs_find_filesystem(fs->name) == NULL && "Filesytem already registered."); pr_debug("vfs_register_filesystem(name: %s)\n", fs->name); + list_head_insert_before(&fs->list, &vfs_filesystems); return 1; } -int vfs_unregister_filesystem(file_system_type *fs) +int vfs_unregister_filesystem(file_system_type_t *fs) { - if (hashmap_remove(vfs_filesystems, (void *)fs->name) != NULL) { - pr_err("Filesystem not present to unregister.\n"); - return 0; - } pr_debug("vfs_unregister_filesystem(name: %s)\n", fs->name); + list_head_remove(&fs->list); return 1; } -int vfs_register_superblock(const char *name, const char *path, file_system_type *type, vfs_file_t *root) +/// @brief Logs the details of a superblock at the specified log level. +/// @param log_level Logging level to use for the output. +/// @param sb Pointer to the `super_block_t` structure to dump. +static inline void __vfs_dump_superblock(int log_level, super_block_t *sb) { - pr_debug("vfs_register_superblock(name: %s, path: %s, type: %s)\n", name, path, type->name); + assert(sb && "Received NULL suberblock."); + pr_log(log_level, "\tname=%s, path=%s, root=%p, type=%p\n", sb->name, sb->path, (void *)sb->root, (void *)sb->type); +} + +void vfs_dump_superblocks(int log_level) +{ + list_for_each_decl(it, &vfs_super_blocks) + { + __vfs_dump_superblock(log_level, list_entry(it, super_block_t, mounts)); + } +} + +int vfs_register_superblock(const char *name, const char *path, file_system_type_t *type, vfs_file_t *root) +{ + pr_debug("vfs_register_superblock(name: %s, path: %s, type: %s, root: %p)\n", name, path, type->name, root); + // Validate input parameters. + if (!name) { + pr_err("vfs_register_superblock: NULL name provided\n"); + return 0; + } + if (!path) { + pr_err("vfs_register_superblock: NULL path provided\n"); + return 0; + } + if (!type) { + pr_err("vfs_register_superblock: NULL file system type provided\n"); + return 0; + } + if (!root) { + pr_err("vfs_register_superblock: NULL root file provided\n"); + return 0; + } // Lock the vfs spinlock. spinlock_lock(&vfs_spinlock); // Create the superblock. super_block_t *sb = kmem_cache_alloc(vfs_superblock_cache, GFP_KERNEL); - // Check if the superblock was correctly allocated. - assert(sb && "Cannot allocate memory for the superblock.\n"); - // Copy the name. - strcpy(sb->name, name); - // Copy the path. - strcpy(sb->path, path); - // Set the pointer. + if (!sb) { + pr_crit("vfs_register_superblock: Failed to allocate memory for superblock\n"); + spinlock_unlock(&vfs_spinlock); // Unlock before returning. + return 0; + } + // Copy the name of the superblock. + strncpy(sb->name, name, sizeof(sb->name) - 1); + sb->name[sizeof(sb->name) - 1] = '\0'; + + // Copy the mount path of the superblock. + strncpy(sb->path, path, sizeof(sb->path) - 1); + sb->path[sizeof(sb->path) - 1] = '\0'; + + // Initialize the root file and file system type. sb->root = root; - // Set the type. sb->type = type; - // Add the superblock to the list. - list_head_insert_after(&sb->mounts, &vfs_super_blocks); - pr_debug("Superblocks:\n"); - list_for_each_decl(it, &vfs_super_blocks) - { - super_block_t *_sb = list_entry(it, super_block_t, mounts); - pr_debug(" Name: %-12s, Path: %-12s, Type: %-12s\n", _sb->name, _sb->path, _sb->type->name); - } - pr_debug("\n"); + // Insert the superblock into the global list of superblocks. + list_head_insert_after(&sb->mounts, &vfs_super_blocks); // Unlock the vfs spinlock. spinlock_unlock(&vfs_spinlock); - return 1; + + return 1; // Return success. } int vfs_unregister_superblock(super_block_t *sb) @@ -200,16 +237,40 @@ vfs_file_t *vfs_open(const char *path, int flags, mode_t mode) int vfs_close(vfs_file_t *file) { + // Check for null file pointer. + if (file == NULL) { + pr_err("vfs_close: Invalid file pointer (NULL).\n"); + return -EINVAL; + } + + // Check for valid fs_operations pointer. + if (file->fs_operations == NULL) { + pr_err("vfs_close: No fs_operations provided for file \"%s\" (ino: %d).\n", file->name, file->ino); + return -EFAULT; + } + pr_debug("vfs_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count - 1); - assert(file->count > 0); - // Close file if it's the last reference. - if (--file->count == 0) { - // Check if the filesystem has the close function. - if (file->fs_operations->close_f == NULL) { - return -ENOSYS; - } - file->fs_operations->close_f(file); + + // Ensure reference count is greater than zero. + if (file->count <= 0) { + pr_crit("vfs_close: Invalid reference count (%d) for file \"%s\" (ino: %d).\n", file->count, file->name, file->ino); + return -EINVAL; } + + // Check if the filesystem has a close function. + if (file->fs_operations->close_f == NULL) { + pr_warning("vfs_close: Filesystem does not support close operation for file \"%s\" (ino: %d).\n", file->name, file->ino); + return -ENOSYS; + } + + int ret = file->fs_operations->close_f(file); + if (ret < 0) { + pr_err("vfs_close: Filesystem close function failed for file \"%s\" (ino: %d) with error %d.\n", file->name, file->ino, ret); + return ret; // Return the specific error from close_f + } + + pr_debug("vfs_close: Successfully closed file.\n"); + return 0; } @@ -249,7 +310,7 @@ ssize_t vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count) return file->fs_operations->getdents_f(file, dirp, off, count); } -int vfs_ioctl(vfs_file_t *file, int request, void *data) +long vfs_ioctl(vfs_file_t *file, unsigned int request, unsigned long data) { if (file->fs_operations->ioctl_f == NULL) { pr_err("No IOCTL function found for the current filesystem.\n"); @@ -258,6 +319,15 @@ int vfs_ioctl(vfs_file_t *file, int request, void *data) return file->fs_operations->ioctl_f(file, request, data); } +long vfs_fcntl(vfs_file_t *file, unsigned int request, unsigned long data) +{ + if (file->fs_operations->fcntl_f == NULL) { + pr_err("No FCNTL function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->fcntl_f(file, request, data); +} + int vfs_unlink(const char *path) { // Allocate a variable for the path. @@ -456,7 +526,7 @@ int vfs_stat(const char *path, stat_t *buf) // If the first character is not the '/' then get the absolute path. int ret = resolve_path(path, absolute_path, PATH_MAX, REMOVE_TRAILING_SLASH); if (ret < 0) { - pr_err("vfs_stat(%s): Cannot get the absolute path.", path); + pr_err("vfs_stat(%s): Cannot get the absolute path.\n", path); return ret; } super_block_t *sb = vfs_get_superblock(absolute_path); @@ -466,12 +536,12 @@ int vfs_stat(const char *path, stat_t *buf) } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_stat(%s): Cannot find the superblock root.", path); + pr_err("vfs_stat(%s): Cannot find the superblock root.\n", path); return -ENOENT; } // Check if the function is implemented. if (sb_root->sys_operations->stat_f == NULL) { - pr_err("vfs_stat(%s): Function not supported in current filesystem.", path); + pr_err("vfs_stat(%s): Function not supported in current filesystem.\n", path); return -ENOSYS; } // Reset the structure. @@ -501,7 +571,7 @@ int vfs_fstat(vfs_file_t *file, stat_t *buf) int vfs_mount(const char *type, const char *path, const char *args) { - file_system_type *fst = (file_system_type *)hashmap_get(vfs_filesystems, type); + file_system_type_t *fst = __vfs_find_filesystem(type); if (fst == NULL) { pr_err("Unknown filesystem type: %s\n", type); return -ENODEV; @@ -616,6 +686,10 @@ int vfs_dup_task(task_struct *task, task_struct *old_task) pr_err("Error while trying to create proc entry for '%d': %s\n", task->pid, strerror(errno)); return 0; } + if (vfs_update_pipe_counts(task, old_task)) { + pr_err("Error while updating the pipe count for '%d': %s\n", task->pid, strerror(errno)); + return 0; + } return 1; } diff --git a/mentos/src/hardware/timer.c b/mentos/src/hardware/timer.c index 70b4ad09..ae5831e4 100644 --- a/mentos/src/hardware/timer.c +++ b/mentos/src/hardware/timer.c @@ -22,7 +22,7 @@ #include "process/scheduler.h" #include "process/wait.h" #include "stdint.h" -#include "sys/errno.h" +#include "errno.h" #include "system/signal.h" #include "system/panic.h" #include "string.h" @@ -399,10 +399,12 @@ static inline void __sleep_data_dealloc(sleep_data_t *sleep_data) /// @param timer where the final values should be stored. static inline void __values_to_itimerval(time_t interval, time_t value, struct itimerval *timer) { + // Validate input. + assert(timer && "__timespec_to_ticks: Input timer is NULL."); timer->it_interval.tv_sec = interval / TICKS_PER_SECOND; - timer->it_interval.tv_usec = (interval * 1000) / TICKS_PER_SECOND; + timer->it_interval.tv_usec = (interval % TICKS_PER_SECOND) * 1000000 / TICKS_PER_SECOND; timer->it_value.tv_sec = value / TICKS_PER_SECOND; - timer->it_value.tv_usec = (value * 1000) / TICKS_PER_SECOND; + timer->it_value.tv_usec = (value % TICKS_PER_SECOND) * 1000000 / TICKS_PER_SECOND; } /// @brief Transforms an interval timer to values. @@ -411,10 +413,36 @@ static inline void __values_to_itimerval(time_t interval, time_t value, struct i /// @param timer the timer used to initialize the values. static inline void __itimerval_to_values(time_t *interval, time_t *value, const struct itimerval *timer) { - *interval = (timer->it_interval.tv_sec * TICKS_PER_SECOND) + - (timer->it_interval.tv_usec * TICKS_PER_SECOND) / 1000; - *value = (timer->it_value.tv_sec * TICKS_PER_SECOND) + - (timer->it_value.tv_usec * TICKS_PER_SECOND) / 1000; + // Validate input. + assert(interval && "__timespec_to_ticks: Input interval is NULL."); + assert(value && "__timespec_to_ticks: Input value is NULL."); + assert(timer && "__timespec_to_ticks: Input timer is NULL."); + *interval = (timer->it_interval.tv_sec * TICKS_PER_SECOND) + (timer->it_interval.tv_usec * TICKS_PER_SECOND) / 1000000; + *value = (timer->it_value.tv_sec * TICKS_PER_SECOND) + (timer->it_value.tv_usec * TICKS_PER_SECOND) / 1000000; +} + +/// @brief Convert timespec to ticks. +/// @param ts Pointer to the timespec structure to be converted. +/// @return The equivalent number of ticks. +static inline unsigned long __timespec_to_ticks(const timespec_t *ts) +{ + // Validate input. + assert(ts && "__timespec_to_ticks: Input ts is NULL."); + // Convert seconds to ticks and add the conversion for nanoseconds. + return (unsigned long)(ts->tv_sec * TICKS_PER_SECOND) + + (unsigned long)(ts->tv_nsec / (1000000000 / TICKS_PER_SECOND)); +} + +/// @brief Convert timeval to ticks. +/// @param tv Pointer to the timeval structure to be converted. +/// @return The equivalent number of ticks. +static inline unsigned long __timeval_to_ticks(const timeval_t *tv) +{ + // Validate input. + assert(tv && "__timeval_to_ticks: Input ts is NULL."); + // Convert seconds to ticks and add the conversion for microseconds. + return (unsigned long)(tv->tv_sec * TICKS_PER_SECOND) + + (unsigned long)(tv->tv_usec / (1000000 / TICKS_PER_SECOND)); } /// @brief Updates the timer for the given task. @@ -452,10 +480,8 @@ void dynamic_timers_install(void) { // Initialize the timer structure for each CPU. __tvec_base_init(&cpu_base); - // Initialize sleeping process list - list_head_init(&sleep_queue.task_list); - // Initialize the sleep queue lock. - spinlock_init(&sleep_queue.lock); + // Initialize wait queue. + wait_queue_head_init(&sleep_queue); } void run_timer_softirq(void) @@ -615,12 +641,12 @@ int sys_nanosleep(const struct timespec *req, struct timespec *rem) sleep_data->remaining = rem; sleep_data->wait_queue_entry = sleep_on(&sleep_queue); // Setup the timer. - sleep_timer->expires = timer_get_ticks() + TICKS_PER_SECOND * req->tv_sec; + sleep_timer->expires = timer_get_ticks() + __timespec_to_ticks(req); sleep_timer->function = &sleep_timeout; sleep_timer->data = (unsigned long)sleep_data; // Add the timer. add_timer(sleep_timer); - return -1; + return 0; } unsigned sys_alarm(int seconds) @@ -688,11 +714,10 @@ int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval sys_getitimer(which, old_value); } // Get ticks of interval. - time_t new_interval = (new_value->it_interval.tv_sec * TICKS_PER_SECOND) + - (new_value->it_interval.tv_usec * TICKS_PER_SECOND) / 1000; + time_t new_interval_ticks = __timeval_to_ticks(&new_value->it_interval); // If interval is 0 removes timer struct task_struct *task = scheduler_get_current_process(); - if (new_interval == 0) { + if (new_interval_ticks == 0) { // Removes real_timer. if ((which == ITIMER_REAL) && (task->real_timer != NULL)) { __timer_list_dealloc(task->real_timer); @@ -703,23 +728,25 @@ int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval } switch (which) { // Uses Dynamic Timers - case ITIMER_REAL: { - // Remove real_timer if already in use. - if (task->real_timer) { - remove_timer(task->real_timer); - } else { - // Alloc new timer - task->real_timer = __timer_list_alloc(); + case ITIMER_REAL: + { + // Remove real_timer if already in use. + if (task->real_timer) { + remove_timer(task->real_timer); + } else { + // Alloc new timer + task->real_timer = __timer_list_alloc(); + } + // Initialize the timer. + init_timer(task->real_timer); + // Setup the timer. + task->real_timer->expires = timer_get_ticks() + new_interval_ticks; + task->real_timer->function = &real_timer_timeout; + task->real_timer->data = (unsigned long)task; + // Add the timer. + add_timer(task->real_timer); } - // Initialize the timer. - init_timer(task->real_timer); - // Setup the timer. - task->real_timer->expires = timer_get_ticks() + new_interval; - task->real_timer->function = &real_timer_timeout; - task->real_timer->data = (unsigned long)task; - // Add the timer. - add_timer(task->real_timer); - } break; + break; case ITIMER_VIRTUAL: case ITIMER_PROF: diff --git a/mentos/src/io/debug.c b/mentos/src/io/debug.c index f052b293..a9635279 100644 --- a/mentos/src/io/debug.c +++ b/mentos/src/io/debug.c @@ -17,18 +17,6 @@ /// Determines the log level. static int max_log_level = LOGLEVEL_DEBUG; -void dbg_putchar(char c) -{ - outportb(SERIAL_COM1, (uint8_t)c); -} - -void dbg_puts(const char *s) -{ - while ((*s) != 0) { - dbg_putchar(*s++); - } -} - /// @brief Prints the correct header for the given debug level. /// @param file the file origin of the debug message. /// @param fun the function where the debug message was called. @@ -67,7 +55,7 @@ static inline void __debug_print_header(const char *file, const char *fun, int l // Print the file and line. sprintf(tmp_prefix, "%s:%d", file, line); // Print the message. - sprintf(final_prefix, " %-20s ", tmp_prefix); + sprintf(final_prefix, " %-40s ", tmp_prefix); // Print the actual message. dbg_puts(final_prefix); #if 0 @@ -96,6 +84,48 @@ int get_log_level(void) return max_log_level; } +const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) { + dblBytes = bytes / 1024.0; + } + } + sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); + return output; +} + +const char *dec_to_binary(unsigned long value, unsigned length) +{ + static char buffer[33]; + // Adjust the length. + length = min(max(0, length), 32U); + // Build the binary. + for (unsigned i = 0, j = 32U - length; j < 32U; ++i, ++j) { + buffer[i] = bit_check(value, 31 - j) ? '1' : '0'; + } + // Close the string. + buffer[length] = 0; + return buffer; +} + +void dbg_putchar(char c) +{ + outportb(SERIAL_COM1, (uint8_t)c); +} + +void dbg_puts(const char *s) +{ + while ((*s) != 0) { + dbg_putchar(*s++); + } +} + void dbg_printf(const char *file, const char *fun, int line, char *header, short log_level, const char *format, ...) { // Define a buffer for the formatted string. @@ -135,59 +165,3 @@ void dbg_printf(const char *file, const char *fun, int line, char *header, short } } } - -const char *to_human_size(unsigned long bytes) -{ - static char output[200]; - const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; - char length = sizeof(suffix) / sizeof(suffix[0]); - int i = 0; - double dblBytes = bytes; - if (bytes > 1024) { - for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) { - dblBytes = bytes / 1024.0; - } - } - sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); - return output; -} - -const char *dec_to_binary(unsigned long value, unsigned length) -{ - static char buffer[33]; - // Adjust the length. - length = min(max(0, length), 32U); - // Build the binary. - for (unsigned i = 0, j = 32U - length; j < 32U; ++i, ++j) { - buffer[i] = bit_check(value, 31 - j) ? '1' : '0'; - } - // Close the string. - buffer[length] = 0; - return buffer; -} - -/// @brief Prints the registers. -/// @param frame the registers to print. -void dbg_print_regs(pt_regs *frame) -{ - pr_debug("Interrupt stack frame:\n"); - pr_debug("GS = 0x%-04x\n", frame->gs); - pr_debug("FS = 0x%-04x\n", frame->fs); - pr_debug("ES = 0x%-04x\n", frame->es); - pr_debug("DS = 0x%-04x\n", frame->ds); - pr_debug("EDI = 0x%-09x\n", frame->edi); - pr_debug("ESI = 0x%-09x\n", frame->esi); - pr_debug("EBP = 0x%-09x\n", frame->ebp); - pr_debug("ESP = 0x%-09x\n", frame->esp); - pr_debug("EBX = 0x%-09x\n", frame->ebx); - pr_debug("EDX = 0x%-09x\n", frame->edx); - pr_debug("ECX = 0x%-09x\n", frame->ecx); - pr_debug("EAX = 0x%-09x\n", frame->eax); - pr_debug("INT_NO = %-9d\n", frame->int_no); - pr_debug("ERR_CD = %-9d\n", frame->err_code); - pr_debug("EIP = 0x%-09x\n", frame->eip); - pr_debug("CS = 0x%-04x\n", frame->cs); - pr_debug("EFLAGS = 0x%-09x\n", frame->eflags); - pr_debug("UESP = 0x%-09x\n", frame->useresp); - pr_debug("SS = 0x%-04x\n", frame->ss); -} diff --git a/mentos/src/io/proc_feedback.c b/mentos/src/io/proc_feedback.c index 2eff31c3..0ecbc3c8 100644 --- a/mentos/src/io/proc_feedback.c +++ b/mentos/src/io/proc_feedback.c @@ -7,7 +7,7 @@ #include "io/debug.h" #include "process/process.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" /// @brief Reads data from the /proc/feedback file. /// diff --git a/mentos/src/io/proc_ipc.c b/mentos/src/io/proc_ipc.c index 4c1a3077..331c222f 100644 --- a/mentos/src/io/proc_ipc.c +++ b/mentos/src/io/proc_ipc.c @@ -7,7 +7,7 @@ #include "fs/procfs.h" #include "io/debug.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/msg.h" #include "sys/sem.h" #include "sys/shm.h" diff --git a/mentos/src/io/proc_running.c b/mentos/src/io/proc_running.c index 9533ace4..28f286e6 100644 --- a/mentos/src/io/proc_running.c +++ b/mentos/src/io/proc_running.c @@ -11,7 +11,7 @@ #include "process/process.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" /// @brief Returns the character identifying the process state. /// @param state the process state. diff --git a/mentos/src/io/proc_system.c b/mentos/src/io/proc_system.c index 0ac50380..fd1fdc1a 100644 --- a/mentos/src/io/proc_system.c +++ b/mentos/src/io/proc_system.c @@ -9,7 +9,7 @@ #include "process/process.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "version.h" static ssize_t procs_do_uptime(char *buffer, size_t bufsize); diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c index b5dfd7f5..d6841957 100644 --- a/mentos/src/io/proc_video.c +++ b/mentos/src/io/proc_video.c @@ -20,7 +20,7 @@ #include "io/video.h" #include "process/scheduler.h" #include "sys/bitops.h" -#include "sys/errno.h" +#include "errno.h" /// @brief Prints the ring-buffer. /// @param rb the ring-buffer to print. @@ -183,7 +183,7 @@ static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size /// @param request The ioctl request code (e.g., TCGETS, TCSETS). /// @param data Pointer to the data structure for the ioctl request (e.g., termios). /// @return int Returns 0 on success. -static int procv_ioctl(vfs_file_t *file, int request, void *data) +static long procv_ioctl(vfs_file_t *file, unsigned int request, unsigned long data) { task_struct *process = scheduler_get_current_process(); switch (request) { diff --git a/mentos/src/io/stdio.c b/mentos/src/io/stdio.c index cfcab088..0679bc74 100644 --- a/mentos/src/io/stdio.c +++ b/mentos/src/io/stdio.c @@ -9,7 +9,7 @@ #include "io/video.h" #include "stdio.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" int atoi(const char *str) { diff --git a/mentos/src/ipc/msg.c b/mentos/src/ipc/msg.c index 77b48b03..8c4df6c6 100644 --- a/mentos/src/ipc/msg.c +++ b/mentos/src/ipc/msg.c @@ -18,7 +18,7 @@ #include "stdio.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/msg.h" #include "system/panic.h" diff --git a/mentos/src/ipc/sem.c b/mentos/src/ipc/sem.c index 70803d60..b0dc8fd9 100644 --- a/mentos/src/ipc/sem.c +++ b/mentos/src/ipc/sem.c @@ -43,13 +43,12 @@ #include "assert.h" #include "fcntl.h" -#include "klib/list.h" #include "process/process.h" #include "process/scheduler.h" #include "stdio.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" ///@brief A value to compute the semid value. int __sem_id = 0; diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c index 7dd7a06e..be61a2ed 100644 --- a/mentos/src/ipc/shm.c +++ b/mentos/src/ipc/shm.c @@ -20,8 +20,8 @@ #include "stdio.h" #include "stdlib.h" #include "string.h" -#include "sys/errno.h" -#include "sys/list_head.h" +#include "errno.h" +#include "list_head.h" // #include "process/process.h" diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index c54ed0c5..01f7ff07 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -6,7 +6,7 @@ #include "stdio.h" #include "klib/mutex.h" #include "klib/stdatomic.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/reboot.h" /// @brief Powers off the machine. diff --git a/mentos/src/klib/assert.c b/mentos/src/klib/assert.c index 2e932f9f..8d1efba8 100644 --- a/mentos/src/klib/assert.c +++ b/mentos/src/klib/assert.c @@ -7,14 +7,20 @@ #include "stdio.h" #include "system/panic.h" +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[ASSERT]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. + void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) { - char message[1024]; - sprintf(message, - "FILE: %s\n" - "FUNC: %s\n" - "LINE: %d\n\n" - "Assertion `%s` failed.\n", - file, (function ? function : "NO_FUN"), line, assertion); - kernel_panic(message); + pr_emerg("\n=== ASSERTION FAILED ===\n" + "Assertion: %s\n" + "Location : %s:%d\n" + "Function : %s\n\n", + assertion, + file, line, + (function ? function : "Unknown function")); + kernel_panic("Assertion failed."); } diff --git a/mentos/src/klib/hashmap.c b/mentos/src/klib/hashmap.c index b9bfd5bc..5dbf8b73 100644 --- a/mentos/src/klib/hashmap.c +++ b/mentos/src/klib/hashmap.c @@ -1,277 +1,100 @@ /// @file hashmap.c -/// @brief -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. +/// @brief Source file for a hashmap implementation with `char *` keys. -#include "klib/hashmap.h" -#include "assert.h" -#include "mem/slab.h" -#include "string.h" +#include "hashmap.h" -/// @brief Stores information of an entry of the hashmap. -struct hashmap_entry_t { - /// Key of the entry. - char *key; - /// Value of the entry. - void *value; - /// Pointer to the next entry. - struct hashmap_entry_t *next; -}; +#include +#include +#include -/// @brief Stores information of a hashmap. -struct hashmap_t { - /// Hashing function, used to generate hash keys. - hashmap_hash_t hash_func; - /// Comparison function, used to compare hash keys. - hashmap_comp_t hash_comp; - /// Key duplication function, used to duplicate hash keys. - hashmap_dupe_t hash_key_dup; - /// Key deallocation function, used to free the memory occupied by hash keys. - hashmap_free_t hash_key_free; - /// Size of the hashmap. - unsigned int size; - /// List of entries. - hashmap_entry_t **entries; -}; - -/// @brief Allocates the memory for an hasmap. -/// @return the newly allocated hashmap. -static inline hashmap_t *__alloc_hashmap(void) -{ - hashmap_t *hashmap = kmalloc(sizeof(hashmap_t)); - memset(hashmap, 0, sizeof(hashmap_t)); - return hashmap; -} - -/// @brief Allocates the memory for an entry of the hasmap. -/// @return the newly allocated entry of the hashmap. -static inline hashmap_entry_t *__alloc_entry(void) -{ - hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t)); - memset(entry, 0, sizeof(hashmap_entry_t)); - return entry; -} - -/// @brief Frees the memory of an entry of the hasmap. -/// @param entry the entry we destroy. -static inline void __dealloc_entry(hashmap_entry_t *entry) -{ - assert(entry && "Invalid pointer to an entry."); - kfree(entry); -} - -/// @brief Allocates the memory for a number of entries in the hasmap. -/// @param size the number of entries. -/// @return the newly allocated vector of entries in the hashmap. -static inline hashmap_entry_t **__alloc_entries(unsigned int size) -{ - hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size); - memset(entries, 0, sizeof(hashmap_entry_t *) * size); - return entries; -} - -/// @brief Frees the memory of a vector of entries. -/// @param entries the vector of entries. -static inline void __dealloc_entries(hashmap_entry_t **entries) -{ - assert(entries && "Invalid pointer to entries."); - kfree(entries); -} - -unsigned int hashmap_int_hash(const void *key) -{ - return (unsigned int)key; -} - -int hashmap_int_comp(const void *a, const void *b) +size_t hash(const char *key) { - return (int)a == (int)b; -} - -unsigned int hashmap_str_hash(const void *_key) -{ - unsigned int hash = 0; - const char *key = (const char *)_key; - char c; - // This is the so-called "sdbm" hash. It comes from a piece of public - // domain code from a clone of ndbm. + size_t hash = 5381; + int c; while ((c = *key++)) { - hash = c + (hash << 6) + (hash << 16) - hash; + hash = ((hash << 5) + hash) + c; // hash * 33 + c } - return hash; + return hash % HASHMAP_SIZE; } -int hashmap_str_comp(const void *a, const void *b) +void hashmap_init(hashmap_t *map, hashmap_entry_t *(*alloc_fn)(void), void (*dealloc_fn)(hashmap_entry_t *)) { - return !strcmp(a, b); + assert(map && "Hashmap is NULL."); + memset(map->buckets, 0, sizeof(map->buckets)); + map->alloc_entry = alloc_fn; + map->dealloc_entry = dealloc_fn; } -void *hashmap_do_not_duplicate(const void *value) +void hashmap_insert(hashmap_t *map, const char *key, void *value) { - return (void *)value; -} + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); -void hashmap_do_not_free(void *value) -{ - (void)value; -} + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *new_entry = map->alloc_entry(); + assert(new_entry && "Failed to allocate memory for hashmap entry."); -hashmap_t *hashmap_create( - unsigned int size, - hashmap_hash_t hash_fun, - hashmap_comp_t comp_fun, - hashmap_dupe_t dupe_fun, - hashmap_free_t key_free_fun) -{ - // Allocate the map. - hashmap_t *map = __alloc_hashmap(); - // Initialize the entries. - map->size = size; - map->entries = __alloc_entries(size); - // Initialize its functions. - map->hash_func = hash_fun; - map->hash_comp = comp_fun; - map->hash_key_dup = dupe_fun; - map->hash_key_free = key_free_fun; - return map; + new_entry->hash = hashed_key; + new_entry->value = value; + new_entry->next = map->buckets[index]; + map->buckets[index] = new_entry; } -void hashmap_free(hashmap_t *map) +void *hashmap_get(hashmap_t *map, const char *key) { - for (unsigned int i = 0; i < map->size; ++i) { - hashmap_entry_t *x = map->entries[i], *p; - while (x) { - p = x; - x = x->next; - map->hash_key_free(p->key); - __dealloc_entry(p); - } - } - __dealloc_entries(map->entries); -} + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); -void *hashmap_set(hashmap_t *map, const void *key, void *value) -{ - unsigned int hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *entry = map->buckets[index]; - if (x == NULL) { - hashmap_entry_t *e = __alloc_entry(); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - map->entries[hash] = e; - - return NULL; - } - - hashmap_entry_t *p = NULL; - - do { - if (map->hash_comp(x->key, key)) { - void *out = x->value; - x->value = value; - - return out; - } - p = x; - x = x->next; - } while (x); - - hashmap_entry_t *e = __alloc_entry(); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - p->next = e; - - return NULL; -} - -void *hashmap_get(hashmap_t *map, const void *key) -{ - unsigned int hash = map->hash_func(key) % map->size; - for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) { - if (map->hash_comp(x->key, key)) { - { - return x->value; - } + while (entry != NULL) { + if (entry->hash == hashed_key) { + return entry->value; } + entry = entry->next; } - return NULL; + return NULL; // Key not found } -void *hashmap_remove(hashmap_t *map, const void *key) +void hashmap_remove(hashmap_t *map, const char *key) { - unsigned int hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; + assert(map && "Hashmap is NULL."); + assert(key && "Key is NULL."); - if (x == NULL) { - return NULL; - } - if (map->hash_comp(x->key, key)) { - void *out = x->value; - map->entries[hash] = x->next; - map->hash_key_free(x->key); - __dealloc_entry(x); - return out; - } + size_t hashed_key = hash(key); + size_t index = hashed_key % HASHMAP_SIZE; + hashmap_entry_t *entry = map->buckets[index]; + hashmap_entry_t *prev = NULL; - hashmap_entry_t *p = x; - x = x->next; - do { - if (map->hash_comp(x->key, key)) { - void *out = x->value; - p->next = x->next; - map->hash_key_free(x->key); - __dealloc_entry(x); - return out; - } - p = x; - x = x->next; - } while (x); - - return NULL; -} - -int hashmap_is_empty(hashmap_t *map) -{ - for (unsigned int i = 0; i < map->size; ++i) { - if (map->entries[i]) { - return 0; - } - } - return 1; -} - -int hashmap_has(hashmap_t *map, const void *key) -{ - unsigned int hash = map->hash_func(key) % map->size; - for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) { - if (map->hash_comp(x->key, key)) { - return 1; + while (entry != NULL) { + if (entry->hash == hashed_key) { + if (prev == NULL) { + map->buckets[index] = entry->next; + } else { + prev->next = entry->next; + } + map->dealloc_entry(entry); + return; } + prev = entry; + entry = entry->next; } - return 0; } -list_t *hashmap_keys(hashmap_t *map) +void hashmap_destroy(hashmap_t *map) { - list_t *l = list_create(); - for (unsigned int i = 0; i < map->size; ++i) { - for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) { - list_insert_back(l, x->key); - } - } - return l; -} + assert(map && "Hashmap is NULL."); -list_t *hashmap_values(hashmap_t *map) -{ - list_t *l = list_create(); - for (unsigned int i = 0; i < map->size; ++i) { - for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) { - list_insert_back(l, x->value); + for (int i = 0; i < HASHMAP_SIZE; i++) { + hashmap_entry_t *entry = map->buckets[i]; + while (entry != NULL) { + hashmap_entry_t *next_entry = entry->next; + map->dealloc_entry(entry); + entry = next_entry; } + map->buckets[i] = NULL; } - return l; } diff --git a/mentos/src/klib/list.c b/mentos/src/klib/list.c index 75cb5c5c..61666135 100644 --- a/mentos/src/klib/list.c +++ b/mentos/src/klib/list.c @@ -1,93 +1,26 @@ /// @file list.c -/// @brief -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. +/// @brief Source file for list operations, implementing list manipulation functions. -#include "klib/list.h" -#include "assert.h" -#include "mem/slab.h" -#include "string.h" +#include "list.h" -/// @brief Allocates the memory for a node. -/// @return a pointer to the newly allocated node. -static inline listnode_t *__node_alloc(void) -{ - listnode_t *node = kmalloc(sizeof(listnode_t)); - memset(node, 0, sizeof(listnode_t)); - return node; -} - -/// @brief Frees the memory of a node. -/// @param node the new we want to free. -static inline void __node_dealloc(listnode_t *node) -{ - assert(node && "Invalid pointer to node."); - kfree(node); -} - -/// @brief Allocates the memory for a list. -/// @return a pointer to the newly allocated list. -static inline list_t *__list_alloc(void) -{ - // Allocate the list. - list_t *list = kmalloc(sizeof(list_t)); - assert(list && "Failed to allocate memory for a list."); - // Clear the memory of the list. - memset(list, 0, sizeof(list_t)); - return list; -} - -/// @brief Frees the memory of a list. -/// @param list the list we want to free. -static inline void __list_dealloc(list_t *list) -{ - assert(list && "Invalid pointer to list."); - kfree(list); -} - -list_t *list_create(void) -{ - return __list_alloc(); -} - -unsigned int list_size(list_t *list) +void list_init(list_t *list, listnode_t *(*alloc_fn)(void), void (*dealloc_fn)(listnode_t *)) { assert(list && "List is null."); - - return list->size; -} - -int list_empty(list_t *list) -{ - assert(list && "List is null."); - - if (list->size == 0) { - return 1; - } - - return 0; + list_head_init(&list->head); + list->size = 0; + list->alloc = alloc_fn; + list->dealloc = dealloc_fn; } listnode_t *list_insert_front(list_t *list, void *value) { assert(list && "List is null."); assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = __node_alloc(); - - list->head->prev = node; - node->next = list->head; - node->value = value; - - // If it's the first element, then it's both head and tail - if (!list->head) { - list->tail = node; - } - - list->head = node; + listnode_t *node = list->alloc(); + assert(node && "Failed to allocate node."); + node->value = value; + list_head_insert_after(&node->list, &list->head); list->size++; - return node; } @@ -95,21 +28,10 @@ listnode_t *list_insert_back(list_t *list, void *value) { assert(list && "List is null."); assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = __node_alloc(); - node->prev = list->tail; - - if (list->tail != NULL) { - list->tail->next = node; - } + listnode_t *node = list->alloc(); + assert(node && "Failed to allocate node."); node->value = value; - - if (list->head == NULL) { - list->head = node; - } - - list->tail = node; + list_head_insert_before(&node->list, &list->head); list->size++; return node; } @@ -118,230 +40,96 @@ void *list_remove_node(list_t *list, listnode_t *node) { assert(list && "List is null."); assert(node && "Node is null."); - - if (list->head == node) { - return list_remove_front(list); - } - if (list->tail == node) { - return list_remove_back(list); - } - - void *value = node->value; - node->next->prev = node->prev; - node->prev->next = node->next; + void *value = node->value; + list_head_remove(&node->list); + list->dealloc(node); list->size--; - __node_dealloc(node); - return value; } void *list_remove_front(list_t *list) { assert(list && "List is null."); - - if (list->head == NULL) { + if (list_head_empty(&list->head)) { return NULL; } - - listnode_t *node = list->head; - void *value = node->value; - list->head = node->next; - - if (list->head) { - list->head->prev = NULL; - } - __node_dealloc(node); - list->size--; - - return value; + listnode_t *node = list_entry(list->head.next, listnode_t, list); + return list_remove_node(list, node); } void *list_remove_back(list_t *list) { assert(list && "List is null."); - - if (list->head == NULL) { - return NULL; - } - - listnode_t *node = list->tail; - void *value = node->value; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - __node_dealloc(node); - list->size--; - - return value; -} - -listnode_t *list_find(list_t *list, void *value) -{ - listnode_foreach(listnode, list) - { - if (listnode->value == value) { - return listnode; - } - } - - return NULL; -} - -void list_push_back(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_back(list, value); -} - -listnode_t *list_pop_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->tail; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - - list->size--; - - return node; -} - -listnode_t *list_pop_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->head; - list->head = list->head->next; - list->size--; - - return node; -} - -void list_push_front(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_front(list, value); -} - -void *list_peek_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - return list->head->value; -} - -void *list_peek_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->tail) { + if (list_head_empty(&list->head)) { return NULL; } - - return list->tail->value; + listnode_t *node = list_entry(list->head.prev, listnode_t, list); + return list_remove_node(list, node); } -int list_get_index_of_value(list_t *list, void *value) +void list_destroy(list_t *list) { assert(list && "List is null."); - assert(value && "Value is null."); - - int idx = 0; - listnode_foreach(listnode, list) + list_for_each_safe_decl(entry, store, &list->head) { - if (listnode->value == value) { - return idx; - } - ++idx; + listnode_t *it = list_entry(entry, listnode_t, list); + list->dealloc(it); } - - return -1; + list_head_init(&list->head); + list->size = 0; } -listnode_t *list_get_node_by_index(list_t *list, unsigned int index) +listnode_t *list_find(list_t *list, void *value) { assert(list && "List is null."); - - if (index >= list_size(list)) { - return NULL; - } - - unsigned int curr = 0; - listnode_foreach(listnode, list) + assert(value && "Value is null."); + list_for_each_safe_decl(entry, store, &list->head) { - if (index == curr) { - return listnode; + listnode_t *it = list_entry(entry, listnode_t, list); + if (it->value == value) { + return it; } - curr++; } return NULL; } -void *list_remove_by_index(list_t *list, unsigned int index) +void *list_peek_front(const list_t *list) { assert(list && "List is null."); - - listnode_t *node = list_get_node_by_index(list, index); - if (node != NULL) { - return list_remove_node(list, node); + if (list_empty(list)) { + return NULL; } - - return NULL; + listnode_t *front_node = list_entry(list->head.next, listnode_t, list); + return front_node->value; } -void list_destroy(list_t *list) +void *list_peek_back(const list_t *list) { assert(list && "List is null."); - - // Deallocate each node. - listnode_t *node = list->head; - while (node != NULL) { - listnode_t *save = node; - node = node->next; - __node_dealloc(save); + if (list_empty(list)) { + return NULL; } - - // Free the list. - __list_dealloc(list); + listnode_t *back_node = list_entry(list->head.prev, listnode_t, list); + return back_node->value; } void list_merge(list_t *target, list_t *source) { assert(target && "Target list is null."); assert(source && "Source list is null."); + if (!list_empty(source)) { + listnode_t *target_last = list_entry(target->head.prev, listnode_t, list); + listnode_t *source_first = list_entry(source->head.next, listnode_t, list); - // Destructively merges source into target. - if (target->tail) { - target->tail->next = source->head; - } else { - target->head = source->head; - } + target_last->list.next = &source_first->list; + source_first->list.prev = &target_last->list; - if (source->tail) { - target->tail = source->tail; - } + listnode_t *source_last = list_entry(source->head.prev, listnode_t, list); + source_last->list.next = &target->head; + target->head.prev = &source_last->list; - target->size += source->size; - __list_dealloc(source); + target->size += source->size; + list_head_init(&source->head); + source->size = 0; + } } diff --git a/mentos/src/klib/ndtree.c b/mentos/src/klib/ndtree.c index 483a4af5..4f92478e 100644 --- a/mentos/src/klib/ndtree.c +++ b/mentos/src/klib/ndtree.c @@ -3,410 +3,296 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "io/debug.h" +#include "ndtree.h" + #include "assert.h" -#include "klib/ndtree.h" -#include "mem/slab.h" -#include "sys/list_head.h" // ============================================================================ -// Tree types. - -/// @brief Stores data about an NDTree node. -struct ndtree_node_t { - /// User provided, used indirectly via ndtree_tree_cmp_f. - void *value; - /// Pointer to the parent. - ndtree_node_t *parent; - /// List of siblings. - list_head siblings; - /// List of children. - list_head children; -}; - -/// @brief Stores data about an NDTree. -struct ndtree_t { - /// Comparison function. - ndtree_tree_cmp_f cmp; - /// Size of the tree. - size_t size; - /// Pointer to the root node. - ndtree_node_t *root; - /// List of orphans. - list_head orphans; -}; - -/// @brief Stores data about an NDTree iterator. -struct ndtree_iter_t { - /// Pointer to the head of the list. - list_head *head; - /// Pointer to the current element of the list. - list_head *current; -}; +// Init functions. -// ============================================================================ -// Default Comparison functions. - -/// @brief Default comparison function. -/// @param self the ndtree. -/// @param a the first element. -/// @param b the second element. -/// @return comparison between the elements. -static inline int __ndtree_tree_node_cmp_ptr_cb(ndtree_t *self, void *a, void *b) +void ndtree_tree_init(ndtree_t *tree, ndtree_tree_compare_f compare_node, ndtree_alloc_node_f alloc_node, ndtree_free_node_f free_node) { - return (a > b) - (a < b); -} + // Validate that the tree and function pointers are not NULL. + assert(tree && "ndtree_tree_init: Variable tree is NULL."); + assert(compare_node && "ndtree_tree_init: Function pointer compare_node is NULL."); + assert(alloc_node && "ndtree_tree_init: Function pointer alloc_node is NULL."); + assert(free_node && "ndtree_tree_init: Function pointer free_node is NULL."); -// ============================================================================ -// Node management functions. + // Initialize tree properties to default values. + tree->size = 0; + tree->root = NULL; + tree->compare_node = compare_node; + tree->alloc_node = alloc_node; + tree->free_node = free_node; -ndtree_node_t *ndtree_node_alloc(void) -{ - return kmalloc(sizeof(ndtree_node_t)); + // Initialize the orphan list head. + list_head_init(&tree->orphans); } -ndtree_node_t *ndtree_node_create(void *value) +void ndtree_node_init(ndtree_node_t *node, void *value) { - ndtree_node_t *node = ndtree_node_alloc(); - node = ndtree_node_init(node, value); - return node; -} + // Validate that the node and value are not NULL. + assert(node && "ndtree_node_init: Variable node is NULL."); + assert(value && "ndtree_node_init: Variable value is NULL."); -ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value) -{ - if (node) { - node->value = value; - node->parent = NULL; - list_head_init(&node->siblings); - list_head_init(&node->children); - } - return node; -} + // Set the node's value and initialize relationships. + node->value = value; + node->parent = NULL; -void ndtree_node_set_value(ndtree_node_t *node, void *value) -{ - if (node && value) { - node->value = value; - } + // Initialize sibling and children lists. + list_head_init(&node->siblings); + list_head_init(&node->children); } -void *ndtree_node_get_value(ndtree_node_t *node) +// ============================================================================ +// Node Management Functions + +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value) { + // Ensure the tree and the allocation function are valid. + assert(tree && "ndtree_create_root: Variable tree is NULL."); + assert(value && "ndtree_create_root: Variable value is NULL."); + + // Allocate a new node for the root using the custom allocator. + ndtree_node_t *node = tree->alloc_node(value); if (node) { - return node->value; - } - return NULL; -} + // Initialize the node with the provided value. + ndtree_node_init(node, value); -void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node) -{ - tree->root = node; - ++tree->size; -} + // Set this node as the tree's root and update the tree size. + tree->root = node; + tree->size = 1; + } -ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value) -{ - ndtree_node_t *node = ndtree_node_create(value); - ndtree_set_root(tree, node); + // Return the newly created root node, or NULL if allocation failed. return node; } -ndtree_node_t *ndtree_get_root(ndtree_t *tree) -{ - return tree->root; -} - void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child) { + // Ensure the tree, parent, and child nodes are valid. + assert(tree && "ndtree_add_child_to_node: Variable tree is NULL."); + assert(parent && "ndtree_add_child_to_node: Variable parent is NULL."); + assert(child && "ndtree_add_child_to_node: Variable child is NULL."); + + // Set the parent of the child node. child->parent = parent; - list_head_insert_after(&child->siblings, &parent->children); + + // Insert the child into the parent's list of children. + list_head_insert_before(&child->siblings, &parent->children); + + // Increment the tree's size to reflect the new node. ++tree->size; } ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value) { - ndtree_node_t *child = ndtree_node_create(value); - ndtree_add_child_to_node(tree, parent, child); + // Ensure the tree, allocation function, and parent node are valid. + assert(tree && "ndtree_create_child_of_node: Variable tree is NULL."); + assert(parent && "ndtree_create_child_of_node: Variable parent is NULL."); + assert(value && "ndtree_create_child_of_node: Variable value is NULL."); + + // Allocate a new node for the child using the custom allocator. + ndtree_node_t *child = tree->alloc_node(value); + if (child) { + // Initialize the child node with the provided value. + ndtree_node_init(child, value); + + // Add the child node to the parent node in the tree structure. + ndtree_add_child_to_node(tree, parent, child); + } + + // Return the newly created child node, or NULL if allocation failed. return child; } unsigned int ndtree_node_count_children(ndtree_node_t *node) { - unsigned int children = 0; - if (node) { - list_for_each_decl(it, &node->children) - { - ++children; - } - } - return children; -} + // Ensure the node is valid. + assert(node && "ndtree_node_count_children: Variable node is NULL."); -void ndtree_node_dealloc(ndtree_node_t *node) -{ - if (node) { - kfree(node); - } + // Return the total count of children. + return list_head_size(&node->children); } // ============================================================================ -// Tree management functions. -ndtree_t *ndtree_tree_alloc(void) -{ - return kmalloc(sizeof(ndtree_t)); -} +// Tree Management Functions -ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp) +/// @brief Recursively deallocates nodes in the tree. +/// @param tree The tree containing the nodes. +/// @param node The current node to deallocate. +/// @param node_cb Optional callback function to invoke before deallocating each node. +static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) { - return ndtree_tree_init(ndtree_tree_alloc(), cmp); -} + // Ensure the tree, node, and free_node function are valid. + assert(tree && "ndtree_tree_dealloc_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_dealloc_rec: Variable node is NULL."); -ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f node_cmp_cb) -{ - if (tree) { - tree->size = 0; - tree->cmp = node_cmp_cb ? node_cmp_cb : __ndtree_tree_node_cmp_ptr_cb; - tree->root = NULL; + // Iterate safely over the list of children and recursively deallocate each child. + list_for_each_safe_decl(it, store, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + __ndtree_tree_dealloc_rec(tree, child, node_cb); } - return tree; -} -/// @brief Recursive deallocation of the memory of the tree. -/// @param tree the tree. -/// @param node the node to deallocate. -/// @param node_cb the callback to call before freeing the memory of the node. -static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) -{ - if (node && node_cb) { - if (!list_head_empty(&node->children)) { - list_head *it_save; - list_for_each_decl(it, &node->children) - { - ndtree_node_t *entry = list_entry(it, ndtree_node_t, siblings); - it_save = it->prev; - list_head_remove(it); - it = it_save; - __ndtree_tree_dealloc_rec(tree, entry, node_cb); - } - } - node_cb(tree, node); - kfree(node); + // Invoke the callback function on the current node, if provided. + if (node_cb) { + node_cb(node); } + + // Deallocate the current node using the custom free function. + tree->free_node(node); } void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb) { - if (tree && tree->root && node_cb) { + // Ensure the tree is valid. + assert(tree && "ndtree_tree_dealloc: Variable tree is NULL."); + + // Check if the tree has a root node to begin deallocation. + if (tree->root) { + // Recursively deallocate all nodes starting from the root. __ndtree_tree_dealloc_rec(tree, tree->root, node_cb); - } - kfree(tree); -} -/// @brief Recursive search in the tree. -/// @param tree the tree. -/// @param cmp the comparison function. -/// @param value the value to compare against. -/// @param node the current node. -/// @return the node we found, NULL otherwise. -static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value, ndtree_node_t *node) -{ - ndtree_node_t *result = NULL; - if (tree && cmp && node && value) { - if (cmp(tree, node->value, value) == 0) { - result = node; - } else if (!list_head_empty(&node->children)) { - list_for_each_decl(it, &node->children) - { - ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); - if ((result = __ndtree_tree_find_rec(tree, cmp, value, child)) != NULL) { - break; - } - } + // Reset the tree's root and size after deallocation. + tree->root = NULL; + tree->size = 0; + + // Iterate safely over the list of children and recursively deallocate + // each orphan. + list_for_each_safe_decl(it, store, &tree->orphans) + { + ndtree_node_t *orphan = list_entry(it, ndtree_node_t, siblings); + __ndtree_tree_dealloc_rec(tree, orphan, node_cb); } } - return result; } -ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value) -{ - if (tree && tree->root && value) { - return __ndtree_tree_find_rec(tree, cmp, value, tree->root); +// ============================================================================ +// Tree Search Functions + +/// @brief Recursively searches for a node with a specified value in the tree. +/// @param tree The tree to search in. +/// @param value The value to search for. +/// @param node The current node in the recursion. +/// @return Pointer to the found node if successful, NULL otherwise. +static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, void *value, ndtree_node_t *node) +{ + // Ensure the tree, comparison function, node, and search value are valid. + assert(tree && "ndtree_tree_find_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_find_rec: Variable node is NULL."); + assert(value && "ndtree_tree_find_rec: Variable value is NULL."); + + // Check if the current node matches the search value using the comparison function. + if (tree->compare_node(node->value, value) == 0) { + return node; // Return the node if a match is found. } - return NULL; -} -ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value) -{ - if (tree && node && value) { - // Check only if the node has children. - if (!list_head_empty(&node->children)) { - // Check which compare function we need to use. - ndtree_tree_cmp_f cmp_fun = cmp ? cmp : tree->cmp; - // If neither the tree nor the function argument are valid, rollback to the - // default comparison function. - if (cmp_fun == NULL) { - cmp_fun = __ndtree_tree_node_cmp_ptr_cb; - } - // Iterate throught the children. - list_for_each_decl(it, &node->children) - { - ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); - if (cmp_fun(tree, child->value, value) == 0) { - return child; - } - } + // Recursively search in each child of the current node. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + ndtree_node_t *result = __ndtree_tree_find_rec(tree, value, child); + if (result) { + return result; // Return the matching node if found in recursion. } } - return NULL; -} -unsigned int ndtree_tree_size(ndtree_t *tree) -{ - if (tree) { - return tree->size; - } - return 0; + // Return NULL if no match is found in the subtree. + return NULL; } -int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, void *value) { - if (tree && node) { - // Remove the node from the parent list. - list_head_remove(&node->siblings); - // If the node has children, we need to migrate them. - if (!list_head_empty(&node->children)) { - // The new parent, by default it is NULL. - ndtree_node_t *new_parent = NULL; - // The new list, by default it is the list of orphans of the tree. - list_head *new_list = &tree->orphans; - // If the found node has a parent, we need to set the variables - // so that we can migrate the children. - if (node->parent) { - new_parent = node->parent; - new_list = &node->parent->children; - } - // Migrate the children. - list_for_each_decl(it, &node->children) - { - ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); - child->parent = new_parent; - } - // Merge the lists. - list_head_append(new_list, &node->children); - } - if (node_cb) { - node_cb(tree, node); - } else { - ndtree_node_dealloc(node); - } - --tree->size; - return 1; - } - return 0; -} + // Ensure the tree, comparison function, node, and search value are valid. + assert(tree && "ndtree_tree_find: Variable tree is NULL."); + assert(tree->root && "ndtree_tree_find: Variable tree->root is NULL."); + assert(value && "ndtree_tree_find: Variable value is NULL."); -int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb) -{ - if (tree && value) { - ndtree_node_t *node = ndtree_tree_find(tree, tree->cmp, value); - return ndtree_tree_remove_node_with_cb(tree, node, node_cb); - } - return 0; + return __ndtree_tree_find_rec(tree, value, tree->root); } // ============================================================================ -// Iterators. -ndtree_iter_t *ndtree_iter_alloc(void) -{ - ndtree_iter_t *iter = kmalloc(sizeof(ndtree_iter_t)); - iter->head = NULL; - iter->current = NULL; - return iter; -} +// Tree Removal Functions -void ndtree_iter_dealloc(ndtree_iter_t *iter) +int ndtree_tree_remove_node(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) { - if (iter) { - kfree(iter); - } -} + // Ensure the tree and node are valid. + assert(tree && "ndtree_tree_remove_node: Variable tree is NULL."); + assert(node && "ndtree_tree_remove_node: Variable node is NULL."); -ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter) -{ - if (node && iter) { - if (!list_head_empty(&node->children)) { - iter->head = &node->children; - iter->current = iter->head->next; - return list_entry(iter->current, ndtree_node_t, siblings); - } - } - return NULL; -} + // Remove the node from its sibling list. + list_head_remove(&node->siblings); -ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter) -{ - if (node && iter) { - if (!list_head_empty(&node->children)) { - iter->head = &node->children; - iter->current = iter->head->prev; - return list_entry(iter->current, ndtree_node_t, siblings); - } - } - return NULL; -} + // If the node has children, reassign them to a new parent or orphan them. + if (!list_head_empty(&node->children)) { + ndtree_node_t *new_parent = node->parent; + list_head *new_list = new_parent ? &new_parent->children : &tree->orphans; -ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter) -{ - if (iter) { - if (iter->current->next != iter->head) { - iter->current = iter->current->next; - return list_entry(iter->current, ndtree_node_t, siblings); + // Reassign each child’s parent and append children to the new list. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + child->parent = new_parent; } + list_head_append(new_list, &node->children); } - return NULL; -} -ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter) -{ - if (iter) { - if (iter->current->next != iter->head) { - iter->current = iter->current->prev; - return list_entry(iter->current, ndtree_node_t, siblings); - } + // Call the callback function if provided. + if (node_cb) { + node_cb(node); } - return NULL; + + // Free the node using the custom deallocator. + tree->free_node(node); + + // Decrement the tree size to reflect the removal. + --tree->size; + + // Return 1 indicating successful removal. + return 1; } // ============================================================================ -// Tree debugging functions. - -/// @brief A visitor function. -/// @param tree the tree. -/// @param node the current node. -/// @param enter_fun the enter function. -/// @param exit_fun the exit function. -static void __ndtree_tree_visitor_iter(ndtree_t *tree, - ndtree_node_t *node, - ndtree_tree_node_f enter_fun, - ndtree_tree_node_f exit_fun) +// Tree Visit Functions + +/// @brief Recursively visits each node in the tree, calling specified enter and exit functions. +/// @param tree The tree containing the nodes to visit. +/// @param node The current node being visited. +/// @param enter_fun Function to call upon entering each node, or NULL to skip. +/// @param exit_fun Function to call upon exiting each node, or NULL to skip. +static void __ndtree_tree_visitor_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) { - assert(tree); - assert(node); + // Ensure that the tree and node are valid. + assert(tree && "ndtree_tree_visitor_rec: Variable tree is NULL."); + assert(node && "ndtree_tree_visitor_rec: Variable node is NULL."); + + // Call the enter function, if provided. if (enter_fun) { - enter_fun(tree, node); + enter_fun(node); } + + // Recursively visit each child node. if (!list_head_empty(&node->children)) { list_for_each_decl(it, &node->children) - __ndtree_tree_visitor_iter(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun); + { + __ndtree_tree_visitor_rec(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun); + } } + + // Call the exit function, if provided, after all children are visited. if (exit_fun) { - exit_fun(tree, node); + exit_fun(node); } } void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) { - if (tree && tree->root) { - __ndtree_tree_visitor_iter(tree, tree->root, enter_fun, exit_fun); + // Ensure the tree is valid. + assert(tree && "ndtree_tree_visitor: Variable tree is NULL."); + + // Start the recursive visitor from the root node, if it exists. + if (tree->root) { + __ndtree_tree_visitor_rec(tree, tree->root, enter_fun, exit_fun); } } diff --git a/mentos/src/klib/strerror.c b/mentos/src/klib/strerror.c index 65a51585..1091e8af 100644 --- a/mentos/src/klib/strerror.c +++ b/mentos/src/klib/strerror.c @@ -14,467 +14,270 @@ char *strerror(int errnum) case 0: strcpy(error, "Success"); break; -#ifdef EPERM case EPERM: strcpy(error, "Operation not permitted"); break; -#endif -#ifdef ENOENT case ENOENT: strcpy(error, "No such file or directory"); break; -#endif -#ifdef ESRCH case ESRCH: strcpy(error, "No such process"); break; -#endif -#ifdef EINTR case EINTR: strcpy(error, "Interrupted system call"); break; -#endif -#ifdef EIO case EIO: strcpy(error, "I/O error"); break; -#endif -#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) case ENXIO: strcpy(error, "No such device or address"); break; -#endif -#ifdef E2BIG case E2BIG: - strcpy(error, "Arg list too long"); + strcpy(error, "Argument list too long"); break; -#endif -#ifdef ENOEXEC case ENOEXEC: strcpy(error, "Exec format error"); break; -#endif -#ifdef EALREADY - case EALREADY: - strcpy(error, "Socket already connected"); - break; -#endif -#ifdef EBADF case EBADF: - strcpy(error, "Bad file number"); + strcpy(error, "Bad file descriptor"); break; -#endif -#ifdef ECHILD case ECHILD: - strcpy(error, "No children"); - break; -#endif -#ifdef EDESTADDRREQ - case EDESTADDRREQ: - strcpy(error, "Destination address required"); + strcpy(error, "No child processes"); break; -#endif -#ifdef EAGAIN case EAGAIN: - strcpy(error, "No more processes"); + strcpy(error, "Resource temporarily unavailable"); break; -#endif -#ifdef ENOMEM case ENOMEM: - strcpy(error, "Not enough space"); + strcpy(error, "Not enough memory"); break; -#endif -#ifdef EACCES case EACCES: strcpy(error, "Permission denied"); break; -#endif -#ifdef EFAULT case EFAULT: strcpy(error, "Bad address"); break; -#endif -#ifdef ENOTBLK case ENOTBLK: strcpy(error, "Block device required"); break; -#endif -#ifdef EBUSY case EBUSY: strcpy(error, "Device or resource busy"); break; -#endif -#ifdef EEXIST case EEXIST: strcpy(error, "File exists"); break; -#endif -#ifdef EXDEV case EXDEV: strcpy(error, "Cross-device link"); break; -#endif -#ifdef ENODEV case ENODEV: strcpy(error, "No such device"); break; -#endif -#ifdef ENOTDIR case ENOTDIR: strcpy(error, "Not a directory"); break; -#endif -#ifdef EHOSTDOWN - case EHOSTDOWN: - strcpy(error, "Host is down"); - break; -#endif -#ifdef EINPROGRESS - case EINPROGRESS: - strcpy(error, "Connection already in progress"); - break; -#endif -#ifdef EISDIR case EISDIR: strcpy(error, "Is a directory"); break; -#endif -#ifdef EINVAL case EINVAL: strcpy(error, "Invalid argument"); break; -#endif -#ifdef EISNAM - case EISNAM: - strcpy(error, "Is a named type file"); - break; -#endif -#ifdef ENETDOWN - case ENETDOWN: - strcpy(error, "Network interface is not configured"); - break; -#endif -#ifdef ENFILE case ENFILE: strcpy(error, "Too many open files in system"); break; -#endif -#ifdef EMFILE case EMFILE: strcpy(error, "Too many open files"); break; -#endif -#ifdef ENOTTY case ENOTTY: - strcpy(error, "Not a character device"); + strcpy(error, "Inappropriate I/O control operation"); break; -#endif -#ifdef ETXTBSY case ETXTBSY: strcpy(error, "Text file busy"); break; -#endif -#ifdef EFBIG case EFBIG: strcpy(error, "File too large"); break; -#endif -#ifdef EHOSTUNREACH - case EHOSTUNREACH: - strcpy(error, "Host is unreachable"); - break; -#endif -#ifdef ENOSPC case ENOSPC: strcpy(error, "No space left on device"); break; -#endif -#ifdef ENOTSUP - case ENOTSUP: - strcpy(error, "Not supported"); - break; -#endif -#ifdef ESPIPE case ESPIPE: strcpy(error, "Illegal seek"); break; -#endif -#ifdef EROFS case EROFS: strcpy(error, "Read-only file system"); break; -#endif -#ifdef EMLINK case EMLINK: strcpy(error, "Too many links"); break; -#endif -#ifdef EPIPE case EPIPE: strcpy(error, "Broken pipe"); break; -#endif -#ifdef EDOM case EDOM: - strcpy(error, "Math argument"); + strcpy(error, "Mathematics argument out of domain"); break; -#endif -#ifdef ERANGE case ERANGE: - strcpy(error, "Result too large"); + strcpy(error, "Result out of range"); + break; + case EDEADLK: + strcpy(error, "Resource deadlock would occur"); + break; + case ENAMETOOLONG: + strcpy(error, "File name too long"); + break; + case ENOLCK: + strcpy(error, "No locks available"); + break; + case ENOSYS: + strcpy(error, "Function not implemented"); + break; + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; + case ELOOP: + strcpy(error, "Too many symbolic links encountered"); break; -#endif -#ifdef ENOMSG case ENOMSG: strcpy(error, "No message of desired type"); break; -#endif -#ifdef EIDRM case EIDRM: strcpy(error, "Identifier removed"); break; -#endif -#ifdef EDEADLK - case EDEADLK: - strcpy(error, "Deadlock"); + case ECHRNG: + strcpy(error, "Channel number out of range"); break; -#endif -#ifdef ENETUNREACH - case ENETUNREACH: - strcpy(error, "Network is unreachable"); + case EL2NSYNC: + strcpy(error, "Level 2 not synchronized"); break; -#endif -#ifdef ENOLCK - case ENOLCK: - strcpy(error, "No lock"); + case EL3HLT: + strcpy(error, "Level 3 halted"); + break; + case EL3RST: + strcpy(error, "Level 3 reset"); + break; + case ELNRNG: + strcpy(error, "Link number out of range"); + break; + case EUNATCH: + strcpy(error, "Protocol driver not attached"); + break; + case ENOCSI: + strcpy(error, "No CSI structure available"); + break; + case EL2HLT: + strcpy(error, "Level 2 halted"); + break; + case EBADE: + strcpy(error, "Invalid exchange"); + break; + case EBADR: + strcpy(error, "Invalid request descriptor"); + break; + case EXFULL: + strcpy(error, "Exchange full"); + break; + case ENOANO: + strcpy(error, "No anode"); + break; + case EBADRQC: + strcpy(error, "Invalid request code"); + break; + case EBADSLT: + strcpy(error, "Invalid slot"); break; -#endif -#ifdef ENOSTR case ENOSTR: - strcpy(error, "Not a stream"); + strcpy(error, "Device not a stream"); + break; + case ENODATA: + strcpy(error, "No data available"); break; -#endif -#ifdef ETIME case ETIME: - strcpy(error, "Stream ioctl timeout"); + strcpy(error, "Timer expired"); break; -#endif -#ifdef ENOSR case ENOSR: - strcpy(error, "No stream resources"); + strcpy(error, "Out of stream resources"); break; -#endif -#ifdef ENONET case ENONET: - strcpy(error, "Machine is not on the network"); + strcpy(error, "Machine not on network"); break; -#endif -#ifdef ENOPKG case ENOPKG: - strcpy(error, "No package"); + strcpy(error, "Package not installed"); break; -#endif -#ifdef EREMOTE case EREMOTE: - strcpy(error, "Resource is remote"); + strcpy(error, "Object is remote"); break; -#endif -#ifdef ENOLINK case ENOLINK: - strcpy(error, "Virtual circuit is gone"); + strcpy(error, "Link severed"); break; -#endif -#ifdef EADV case EADV: strcpy(error, "Advertise error"); break; -#endif -#ifdef ESRMNT case ESRMNT: - strcpy(error, "Srmount error"); + strcpy(error, "SR mount error"); break; -#endif -#ifdef ECOMM case ECOMM: - strcpy(error, "Communication error"); + strcpy(error, "Communication error on send"); break; -#endif -#ifdef EPROTO case EPROTO: strcpy(error, "Protocol error"); break; -#endif -#ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: - strcpy(error, "Unknown protocol"); - break; -#endif -#ifdef EMULTIHOP case EMULTIHOP: strcpy(error, "Multihop attempted"); break; -#endif -#ifdef EBADMSG case EBADMSG: - strcpy(error, "Bad message"); + strcpy(error, "Not a data message"); + break; + case EOVERFLOW: + strcpy(error, "Value too large for data type"); + break; + case ENOTUNIQ: + strcpy(error, "Name not unique on network"); + break; + case EBADFD: + strcpy(error, "File descriptor in bad state"); + break; + case EREMCHG: + strcpy(error, "Remote address changed"); break; -#endif -#ifdef ELIBACC case ELIBACC: strcpy(error, "Cannot access a needed shared library"); break; -#endif -#ifdef ELIBBAD case ELIBBAD: strcpy(error, "Accessing a corrupted shared library"); break; -#endif -#ifdef ELIBSCN case ELIBSCN: - strcpy(error, ".lib section in a.out corrupted"); + strcpy(error, "Corrupted .lib section in a.out"); break; -#endif -#ifdef ELIBMAX case ELIBMAX: - strcpy(error, - "Attempting to link in more shared libraries than system limit"); + strcpy(error, "Exceeded shared library system limit"); break; -#endif -#ifdef ELIBEXEC case ELIBEXEC: - strcpy(error, "Cannot exec a shared library directly"); + strcpy(error, "Cannot execute shared library directly"); break; -#endif -#ifdef ENOSYS - case ENOSYS: - strcpy(error, "Function not implemented"); + case EUCLEAN: + strcpy(error, "Structure needs cleaning"); break; -#endif -#ifdef ENMFILE - case ENMFILE: - strcpy(error, "No more files"); + case ENOTNAM: + strcpy(error, "Not a XENIX named type file"); break; -#endif -#ifdef ENOTEMPTY - case ENOTEMPTY: - strcpy(error, "Directory not empty"); + case ENAVAIL: + strcpy(error, "No XENIX semaphores available"); break; -#endif -#ifdef ENAMETOOLONG - case ENAMETOOLONG: - strcpy(error, "File or path name too long"); + case EISNAM: + strcpy(error, "Is a named type file"); + break; + case EREMOTEIO: + strcpy(error, "Remote I/O error"); + break; + case EDQUOT: + strcpy(error, "Quota exceeded"); + break; + case ENOMEDIUM: + strcpy(error, "No medium found"); + break; + case EMEDIUMTYPE: + strcpy(error, "Wrong medium type"); break; -#endif -#ifdef ELOOP - case ELOOP: - strcpy(error, "Too many symbolic links"); - break; -#endif -#ifdef ENOBUFS - case ENOBUFS: - strcpy(error, "No buffer space available"); - break; -#endif -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - strcpy(error, "Address family not supported by protocol family"); - break; -#endif -#ifdef EPROTOTYPE - case EPROTOTYPE: - strcpy(error, "Protocol wrong type for socket"); - break; -#endif -#ifdef ENOTSOCK - case ENOTSOCK: - strcpy(error, "Socket operation on non-socket"); - break; -#endif -#ifdef ENOPROTOOPT - case ENOPROTOOPT: - strcpy(error, "Protocol not available"); - break; -#endif -#ifdef ESHUTDOWN - case ESHUTDOWN: - strcpy(error, "Can't send after socket shutdown"); - break; -#endif -#ifdef ECONNREFUSED - case ECONNREFUSED: - strcpy(error, "Connection refused"); - break; -#endif -#ifdef EADDRINUSE - case EADDRINUSE: - strcpy(error, "Address already in use"); - break; -#endif -#ifdef ECONNABORTED - case ECONNABORTED: - strcpy(error, "Software caused connection abort"); - break; -#endif -#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) - case EWOULDBLOCK: - strcpy(error, "Operation would block"); - break; -#endif -#ifdef ENOTCONN - case ENOTCONN: - strcpy(error, "Socket is not connected"); - break; -#endif -#ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - strcpy(error, "Socket type not supported"); - break; -#endif -#ifdef EISCONN - case EISCONN: - strcpy(error, "Socket is already connected"); - break; -#endif -#ifdef ECANCELED - case ECANCELED: - strcpy(error, "Operation canceled"); - break; -#endif -#ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: - strcpy(error, "State not recoverable"); - break; -#endif -#ifdef EOWNERDEAD - case EOWNERDEAD: - strcpy(error, "Previous owner died"); - break; -#endif -#ifdef ESTRPIPE - case ESTRPIPE: - strcpy(error, "Streams pipe error"); - break; -#endif -#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) - case EOPNOTSUPP: - strcpy(error, "Operation not supported on socket"); - break; -#endif -#ifdef EMSGSIZE - case EMSGSIZE: - strcpy(error, "Message too long"); - break; -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: - strcpy(error, "Connection timed out"); - break; -#endif -#ifdef ENOTSCHEDULABLE case ENOTSCHEDULABLE: - strcpy(error, "The process cannot be scheduled"); + strcpy(error, "Process cannot be scheduled"); break; -#endif default: strcpy(error, "Unknown error"); break; diff --git a/mentos/src/mem/kheap.c b/mentos/src/mem/kheap.c index 802518d8..5e81909e 100644 --- a/mentos/src/mem/kheap.c +++ b/mentos/src/mem/kheap.c @@ -21,7 +21,7 @@ #include "stdlib.h" #include "string.h" #include "sys/bitops.h" -#include "sys/list_head.h" +#include "list_head.h" /// @brief The size of the heap in bytes, defined as 4 megabytes. #define HEAP_SIZE (4 * M) diff --git a/mentos/src/mem/paging.c b/mentos/src/mem/paging.c index 45e74845..2af22c57 100644 --- a/mentos/src/mem/paging.c +++ b/mentos/src/mem/paging.c @@ -18,10 +18,11 @@ #include "stddef.h" #include "stdint.h" #include "string.h" -#include "sys/list_head.h" -#include "sys/list_head_algorithm.h" +#include "list_head.h" +#include "list_head_algorithm.h" #include "sys/mman.h" #include "system/panic.h" +#include "fs/vfs.h" /// Cache for storing mm_struct. kmem_cache_t *mm_cache; @@ -601,7 +602,7 @@ static void __page_fault_panic(pt_regs *f, uint32_t addr) pr_err("Instruction fetch "); } pr_err("]\n"); - dbg_print_regs(f); + PRINT_REGS(pr_err, f); kernel_panic("Page fault!"); @@ -768,8 +769,6 @@ static inline int __set_pg_entry_frame(page_dir_entry_t *entry, page_table_t *ta // to represent the frame number). entry->frame = phy_addr >> 12u; - pr_debug("Set page directory entry frame to 0x%x for table: %p\n", entry->frame, table); - return 0; // Return 0 on success. } @@ -1414,6 +1413,32 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t off // Get the current task. task_struct *task = scheduler_get_current_process(); + // Get the file descriptor. + vfs_file_descriptor_t *file_descriptor = fget(fd); + if (!file_descriptor) { + pr_err("Invalid file descriptor.\n"); + return NULL; + } + + // Get the actual file. + vfs_file_t *file = file_descriptor->file_struct; + if (!file) { + pr_err("Invalid file.\n"); + return NULL; + } + + stat_t file_stat; + if (vfs_fstat(file, &file_stat) < 0) { + pr_err("Failed to get file stat.\n"); + return NULL; + } + + // Ensure the file size is large enough to map + if ((offset + length) > file_stat.st_size) { + pr_err("File is too small for the requested mapping.\n"); + return NULL; + } + // Check if a specific address was requested for the memory mapping. if (addr && is_valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) { // If the requested address is valid, use it as the starting address. diff --git a/mentos/src/mem/slab.c b/mentos/src/mem/slab.c index 8dbf7b7d..47d50967 100644 --- a/mentos/src/mem/slab.c +++ b/mentos/src/mem/slab.c @@ -17,6 +17,9 @@ #include "mem/slab.h" #include "mem/zone_allocator.h" +/// @brief Enables cache tracing. +// #define ENABLE_CACHE_TRACE + /// @brief Structure to represent an individual memory object within a slab. /// @details This structure is used to manage individual objects allocated from /// the slab. It contains a linked list to connect objects in the cache. @@ -69,70 +72,72 @@ static kmem_cache_t *malloc_blocks[MAX_KMALLOC_CACHE_ORDER]; /// @param flags Allocation flags (e.g., GFP_KERNEL) passed to control memory /// allocation behavior. /// @return 0 on success, -1 on failure. -static int __alloc_slab_page(kmem_cache_t *cachep, gfp_t flags) +static inline int __alloc_slab_page(kmem_cache_t *cachep, gfp_t flags) { - // Allocate the required number of pages for the slab based on cache's - // gfp_order. The higher the gfp_order, the more pages are allocated. + // Validate input parameter. + if (!cachep) { + pr_crit("Invalid cache pointer (NULL), cannot allocate slab page.\n"); + return -1; + } + + // Allocate the required number of pages for the slab based on cache's `gfp_order`. page_t *page = _alloc_pages(flags, cachep->gfp_order); // Check if page allocation failed. if (!page) { - pr_crit("Failed to allocate a new page from slab.\n"); + pr_crit("Failed to allocate a new page for cache `%s`.\n", cachep->name); return -1; } // Initialize the linked lists for the slab page. - // These lists track which objects in the page are free and which are in use. list_head_init(&page->slabs); // Initialize the list of slabs (active objects). list_head_init(&page->slab_freelist); // Initialize the free list (unused objects). // Save a reference to the `kmem_cache_t` structure in the root page. - // This is necessary for freeing arbitrary pointers and tracking cache ownership. page[0].container.slab_cache = cachep; - // Update the slab main page pointer for all child pages (in case the allocation - // consists of multiple pages) to point back to the root page. - // This helps in reconstructing the main slab page when dealing with subpages. - for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + // Update the slab main page pointer for all child pages. + for (unsigned i = 1; i < (1U << cachep->gfp_order); i++) { page[i].container.slab_main_page = page; // Link child pages to the main page. } - // Calculate the total size of the slab (in bytes) by multiplying the page size - // by the number of pages allocated (determined by the cache's gfp_order). - unsigned int slab_size = PAGE_SIZE * (1U << cachep->gfp_order); + // Calculate the total size of the slab (in bytes). + unsigned slab_size = PAGE_SIZE * (1U << cachep->gfp_order); // Update object counters for the page. - // The total number of objects in the slab is determined by the slab size - // divided by the size of each object in the cache. - page->slab_objcnt = slab_size / cachep->size; // Total number of objects. - page->slab_objfree = page->slab_objcnt; // Initially, all objects are free. + page->slab_objcnt = slab_size / cachep->aligned_object_size; // Total number of objects. + page->slab_objfree = page->slab_objcnt; // Initially, all objects are free. - // Get the starting physical address of the allocated slab page. - unsigned int pg_addr = get_virtual_address_from_page(page); + // Get the starting virtual address of the allocated slab page. + unsigned pg_addr = get_virtual_address_from_page(page); - // Check if `get_lowmem_address_from_page` failed. + // Check if `get_virtual_address_from_page` failed. if (!pg_addr) { - pr_crit("Failed to get low memory address for slab page.\n"); + pr_crit("Failed to get virtual address for slab page in cache `%s`.\n", cachep->name); + // Free allocated pages before returning. + if (__free_pages(page) < 0) { + pr_crit("Failed to free allocated pages before returning in cache `%s`.\n", cachep->name); + } return -1; } - // Loop through each object in the slab and initialize its kmem_obj structure. - // Each object is inserted into the free list, indicating that it is available. - for (unsigned int i = 0; i < page->slab_objcnt; i++) { - // Calculate the object's address by adding the offset (i * object size) to the page address. - kmem_obj_t *obj = KMEM_OBJ_FROM_ADDR(pg_addr + cachep->size * i); + // Initialize each object in the slab and insert it into the free list. + for (unsigned i = 0; i < page->slab_objcnt; i++) { + // Calculate the object's address. + kmem_obj_t *obj = KMEM_OBJ_FROM_ADDR(pg_addr + cachep->aligned_object_size * i); - // Insert the object into the slab's free list, making it available for allocation. + // Insert the object into the slab's free list. list_head_insert_after(&obj->objlist, &page->slab_freelist); } // Insert the page into the cache's list of free slab pages. list_head_insert_after(&page->slabs, &cachep->slabs_free); - // Update the cache's total object counters to reflect the new slab. - cachep->total_num += page->slab_objcnt; // Increase the total number of objects in the cache. + // Update the cache's total object counters. + cachep->total_num += page->slab_objcnt; // Increase the total number of objects. cachep->free_num += page->slab_objcnt; // Increase the number of free objects. + pr_debug("Allocated slab page with %u objects for cache `%s`.\n", page->slab_objcnt, cachep->name); return 0; } @@ -143,18 +148,26 @@ static int __alloc_slab_page(kmem_cache_t *cachep, gfp_t flags) /// @param free_num The desired number of free objects in the cache. /// @param flags Allocation flags used for controlling memory allocation behavior (e.g., GFP_KERNEL). /// @return 0 on success, -1 on failure. -static int __kmem_cache_refill(kmem_cache_t *cachep, unsigned int free_num, gfp_t flags) +static int __kmem_cache_refill(kmem_cache_t *cachep, unsigned free_num, gfp_t flags) { + // Check for a valid cache pointer. + if (!cachep) { + pr_crit("Invalid cache pointer (NULL), cannot refill.\n"); + return -1; + } + // Continue allocating slab pages until the cache has at least `free_num` // free objects. while (cachep->free_num < free_num) { // Attempt to allocate a new slab page. If allocation fails, print a // warning and abort the refill process. if (__alloc_slab_page(cachep, flags) < 0) { - pr_crit("Failed to allocate a new slab page, aborting refill\n"); + pr_crit("Failed to allocate a new slab page for cache `%s`, aborting refill.\n", cachep->name); return -1; // Return -1 if page allocation fails. } } + + pr_debug("Successfully refilled cache `%s` to have at least %u free objects.\n", cachep->name, free_num); return 0; } @@ -167,10 +180,12 @@ static int __kmem_cache_refill(kmem_cache_t *cachep, unsigned int free_num, gfp_ /// @return 0 on success, -1 on failure. static int __compute_size_and_order(kmem_cache_t *cachep) { - // Check for invalid or uninitialized object sizes or alignment. - // If `object_size` or `align` is zero, the cache cannot be correctly - // configured. - if (cachep->object_size == 0) { + // Validate inputs. + if (!cachep) { + pr_crit("Invalid cache pointer (NULL).\n"); + return -1; + } + if (cachep->raw_object_size == 0) { pr_crit("Object size is invalid (0), cannot compute cache size and order.\n"); return -1; } @@ -183,20 +198,20 @@ static int __compute_size_and_order(kmem_cache_t *cachep) // The object size is padded based on either the `KMEM_OBJ_OVERHEAD` or the // provided alignment requirement. Ensure that the object size is at least // as large as the overhead and is aligned to the cache's alignment. - cachep->size = round_up( - max(cachep->object_size, KMEM_OBJ_OVERHEAD), // Ensure object size is larger than the overhead. - max(8, cachep->align)); // Ensure alignment is at least 8 bytes for proper memory alignment. + cachep->aligned_object_size = round_up( + max(cachep->raw_object_size, KMEM_OBJ_OVERHEAD), // Ensure object size is larger than the overhead. + max(8, cachep->align)); // Ensure alignment is at least 8 bytes for proper memory alignment. // Check if the computed size is valid. - if (cachep->size == 0) { - pr_crit("Computed object size is invalid (0), cannot proceed with cache allocation.\n"); + if (cachep->aligned_object_size == 0) { + pr_crit("Computed object size is zero; invalid for cache allocation.\n"); return -1; } // Compute the `gfp_order` based on the total object size and page size. // The `gfp_order` determines how many contiguous pages will be allocated // for the slab. - unsigned int size = round_up(cachep->size, PAGE_SIZE) / PAGE_SIZE; + unsigned int size = round_up(cachep->aligned_object_size, PAGE_SIZE) / PAGE_SIZE; // Reset `gfp_order` to 0 before calculating. cachep->gfp_order = 0; @@ -209,16 +224,21 @@ static int __compute_size_and_order(kmem_cache_t *cachep) // Check for a valid `gfp_order`. Ensure that it's within reasonable limits. if (cachep->gfp_order > MAX_BUDDYSYSTEM_GFP_ORDER) { - pr_crit("Calculated gfp_order exceeds system limits (%d).\n", MAX_BUDDYSYSTEM_GFP_ORDER); + pr_crit("Calculated gfp_order (%u) exceeds system limit (%u); limiting to max.\n", + cachep->gfp_order, MAX_BUDDYSYSTEM_GFP_ORDER); cachep->gfp_order = MAX_BUDDYSYSTEM_GFP_ORDER; } // Additional consistency check (optional): // Verify that the calculated gfp_order leads to a valid page allocation size. - if ((cachep->gfp_order == 0) && (cachep->size > PAGE_SIZE)) { - pr_crit("Calculated gfp_order is 0, but object size exceeds one page. Potential issue in size computation.\n"); + if ((cachep->gfp_order == 0) && (cachep->aligned_object_size > PAGE_SIZE)) { + pr_crit("gfp_order is 0 but object size exceeds one page; issue in size calculation.\n"); return -1; } + + pr_debug("Computed aligned object size `%u` and gfp_order `%u` for cache `%s`.\n", + cachep->aligned_object_size, cachep->gfp_order, cachep->name); + return 0; } @@ -246,7 +266,7 @@ static int __kmem_cache_create( unsigned int start_count) // Initial number of objects to populate in the cache. { // Log the creation of a new cache. - pr_info("Creating new cache `%s` with objects of size `%d`.\n", name, size); + pr_info("Creating new cache `%s` with objects of size `%u`.\n", name, size); // Input validation checks. if (!cachep) { @@ -254,18 +274,26 @@ static int __kmem_cache_create( return -1; } if (!name || size == 0) { - pr_crit("Invalid cache name or object size (size = %d).\n", size); + pr_crit("Invalid cache name or object size (size = %u).\n", size); return -1; } // Set up the basic properties of the cache. *cachep = (kmem_cache_t){ - .name = name, - .object_size = size, - .align = align, - .flags = flags, - .ctor = ctor, - .dtor = dtor + // .cache_list = 0, + .name = name, + .aligned_object_size = 0, + .raw_object_size = size, + .align = align, + .total_num = 0, + .free_num = 0, + .flags = flags, + .gfp_order = 0, + .ctor = ctor, + .dtor = dtor, + // .slabs_full = 0, + // .slabs_partial = 0, + // .slabs_free = 0, }; // Initialize the list heads for free, partial, and full slabs. @@ -274,17 +302,23 @@ static int __kmem_cache_create( list_head_init(&cachep->slabs_full); // Compute the object size and gfp_order for slab allocations. - // If there's an issue with size or order computation, this function should handle it internally. - __compute_size_and_order(cachep); + // Validate that size and order are computed successfully. + if (__compute_size_and_order(cachep) < 0) { + pr_crit("Failed to compute size and order for cache `%s`.\n", name); + return -1; + } // Refill the cache with `start_count` objects. - // If the refill fails (due to slab page allocation failure), a warning is logged. - __kmem_cache_refill(cachep, start_count, flags); + // If the refill fails, log a critical warning. + if (__kmem_cache_refill(cachep, start_count, flags) < 0) { + pr_crit("Failed to refill cache `%s` with initial objects.\n", name); + return -1; + } // Insert the cache into the global list of caches. - // No error check needed here as list operations usually don't fail. list_head_insert_after(&cachep->cache_list, &kmem_caches_list); + pr_debug("Successfully created cache `%s`.\n", name); return 0; } @@ -297,16 +331,31 @@ static int __kmem_cache_create( /// @return Pointer to the allocated object, or NULL if allocation fails. static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_page) { + // Validate input parameters. + if (!cachep) { + pr_crit("Invalid cache pointer (NULL).\n"); + return NULL; + } + if (!slab_page) { + pr_crit("Invalid slab_page pointer (NULL).\n"); + return NULL; + } + // Retrieve and remove the first element from the slab's free list. list_head *elem_listp = list_head_pop(&slab_page->slab_freelist); // Check if the free list is empty. if (!elem_listp) { - pr_crit("There are no FREE elements inside the slab_freelist\n"); - return NULL; // Return NULL if no free elements are available. + pr_crit("No free elements in slab freelist for cache `%s`.\n", cachep->name); + return NULL; } // Decrement the count of free objects in the slab page and the cache. + if (slab_page->slab_objfree == 0 || cachep->free_num == 0) { + pr_crit("Free object count underflow detected for cache `%s`.\n", cachep->name); + return NULL; + } + slab_page->slab_objfree--; cachep->free_num--; @@ -315,7 +364,7 @@ static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_p // Check if the kmem object pointer is valid. if (!object) { - pr_crit("The kmem object is invalid\n"); + pr_crit("Invalid kmem object in cache `%s`.\n", cachep->name); return NULL; } @@ -327,7 +376,9 @@ static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_p cachep->ctor(elem); } - return elem; // Return the pointer to the allocated object. + pr_debug("Successfully allocated object 0x%p from cache `%s`.\n", elem, cachep->name); + + return elem; } /// @brief Frees a slab page and updates the associated cache statistics. @@ -340,9 +391,19 @@ static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_p static inline int __kmem_cache_free_slab(kmem_cache_t *cachep, page_t *slab_page) { // Validate input parameters. - if (!cachep || !slab_page) { - pr_crit("Invalid cache or slab_page pointer (NULL).\n"); - return -1; // Return error if either pointer is NULL. + if (!cachep) { + pr_crit("Invalid cache pointer (NULL).\n"); + return -1; + } + if (!slab_page) { + pr_crit("Invalid slab_page pointer (NULL).\n"); + return -1; + } + + // Ensure cache object count is not underflowing + if (cachep->free_num < slab_page->slab_objfree || cachep->total_num < slab_page->slab_objcnt) { + pr_crit("Object count inconsistency detected in cache `%s`.\n", cachep->name); + return -1; } // Update the free and total object counts in the cache. @@ -356,15 +417,20 @@ static inline int __kmem_cache_free_slab(kmem_cache_t *cachep, page_t *slab_page // Reset the main page pointers for all non-root slab pages. This loop // assumes the first page is the root and resets pointers for child pages. - for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + for (unsigned i = 1; i < (1U << cachep->gfp_order); i++) { // Clear main page pointer for each child page. (slab_page + i)->container.slab_main_page = NULL; } // Free the memory associated with the slab page. - __free_pages(slab_page); + if (__free_pages(slab_page) < 0) { + pr_crit("Failed to free slab page memory for cache `%s`.\n", cachep->name); + return -1; + } - return 0; // Return success. + pr_debug("Successfully freed slab page for cache `%s`.\n", cachep->name); + + return 0; } int kmem_cache_init(void) @@ -383,11 +449,11 @@ int kmem_cache_init(void) NULL, 32) < 0) { pr_crit("Failed to create kmem_cache for kmem_cache_t.\n"); - return -1; // Early exit if kmem_cache creation fails. + return -1; } // Create caches for different order sizes for kmalloc allocations. - for (unsigned int i = 0; i < MAX_KMALLOC_CACHE_ORDER; i++) { + for (unsigned i = 0; i < MAX_KMALLOC_CACHE_ORDER; i++) { malloc_blocks[i] = kmem_cache_create( "kmalloc", 1u << i, // Size of the allocation (2^i). @@ -399,11 +465,27 @@ int kmem_cache_init(void) // Check if the cache was created successfully. if (!malloc_blocks[i]) { pr_crit("Failed to create kmalloc cache for order %u.\n", i); - return -1; // Early exit if kmem_cache creation fails. + + // Clean up any previously allocated caches before exiting. + for (unsigned j = 0; j < i; j++) { + if (malloc_blocks[j]) { + if (kmem_cache_destroy(malloc_blocks[j]) < 0) { + pr_crit("Failed to destroy kmalloc cache for order %u.\n", j); + } + malloc_blocks[j] = NULL; + } + } + // Destroy kmem_cache to free resources. + if (kmem_cache_destroy(&kmem_cache) < 0) { + pr_crit("Failed to destroy kmem_cache to free resources %u.\n", i); + } + return -1; } } - return 0; // Return success. + pr_info("kmem_cache system successfully initialized.\n"); + + return 0; } kmem_cache_t *kmem_cache_create( @@ -414,24 +496,42 @@ kmem_cache_t *kmem_cache_create( kmem_fun_t ctor, kmem_fun_t dtor) { + // Check for a valid cache name. + if (!name || !*name) { + pr_crit("Invalid cache name provided.\n"); + return NULL; + } + + // Check for a valid cache size. + if (size == 0) { + pr_crit("Cache size must be greater than zero.\n"); + return NULL; + } + // Allocate memory for a new kmem_cache_t structure. kmem_cache_t *cachep = (kmem_cache_t *)kmem_cache_alloc(&kmem_cache, GFP_KERNEL); // Check if memory allocation for the cache failed. if (!cachep) { pr_crit("Failed to allocate memory for kmem_cache_t.\n"); - return NULL; // Return NULL to indicate failure. + return NULL; } // Initialize the kmem_cache_t structure. if (__kmem_cache_create(cachep, name, size, align, flags, ctor, dtor, KMEM_START_OBJ_COUNT) < 0) { pr_crit("Failed to initialize kmem_cache for '%s'.\n", name); + // Free allocated memory if initialization fails. - kmem_cache_free(cachep); - return NULL; // Return NULL to indicate failure. + if (kmem_cache_free(cachep) < 0) { + pr_crit("Failed to free allocated memory for '%s'.\n", name); + } + + return NULL; } - return cachep; // Return the pointer to the newly created cache. + pr_debug("Successfully created cache '%s'.\n", name); + + return cachep; } int kmem_cache_destroy(kmem_cache_t *cachep) @@ -439,41 +539,61 @@ int kmem_cache_destroy(kmem_cache_t *cachep) // Validate input parameter. if (!cachep) { pr_crit("Cannot destroy a NULL cache pointer.\n"); - return -1; // Early exit if cache pointer is NULL. + return -1; } // Free all slabs in the free list. while (!list_head_empty(&cachep->slabs_free)) { list_head *slab_list = list_head_pop(&cachep->slabs_free); + if (!slab_list) { + pr_crit("Failed to retrieve a slab from free list.\n"); + return -1; + } __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); } // Free all slabs in the partial list. while (!list_head_empty(&cachep->slabs_partial)) { list_head *slab_list = list_head_pop(&cachep->slabs_partial); + if (!slab_list) { + pr_crit("Failed to retrieve a slab from partial list.\n"); + return -1; + } __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); } // Free all slabs in the full list. while (!list_head_empty(&cachep->slabs_full)) { list_head *slab_list = list_head_pop(&cachep->slabs_full); + if (!slab_list) { + pr_crit("Failed to retrieve a slab from full list.\n"); + return -1; + } __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); } // Free the cache structure itself. - kmem_cache_free(cachep); + if (kmem_cache_free(cachep) != 0) { + pr_crit("Failed to free cache structure.\n"); + return -1; + } + // Remove the cache from the global cache list. list_head_remove(&cachep->cache_list); - return 0; // Return success. + pr_debug("Successfully destroyed cache `%s`.\n", cachep->name); + + return 0; } -#ifdef ENABLE_CACHE_TRACE void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cache_t *cachep, gfp_t flags) -#else -void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags) -#endif { + // Check for null cache pointer + if (!cachep) { + pr_err("Null cache pointer provided.\n"); + return NULL; + } + // Check if there are any partially filled slabs. if (list_head_empty(&cachep->slabs_partial)) { // If no partial slabs, check for free slabs. @@ -482,23 +602,25 @@ void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags) if (flags == 0) { flags = cachep->flags; } + // Attempt to refill the cache, limiting the number of objects. if (__kmem_cache_refill(cachep, min(cachep->total_num, KMEM_MAX_REFILL_OBJ_COUNT), flags) < 0) { - pr_crit("Failed to refill cache in `%s`\n", cachep->name); - return NULL; // Return NULL to indicate failure. + pr_crit("Failed to refill cache `%s`\n", cachep->name); + return NULL; } + // If still no free slabs, log an error and return NULL. if (list_head_empty(&cachep->slabs_free)) { pr_crit("Cannot allocate more slabs in `%s`\n", cachep->name); - return NULL; // Return NULL to indicate failure. + return NULL; } } // Move a free slab to the partial list since we're about to allocate from it. list_head *free_slab = list_head_pop(&cachep->slabs_free); if (!free_slab) { - pr_crit("We retrieved an invalid slab from the free list."); - return NULL; // Return NULL to indicate failure. + pr_crit("Retrieved invalid slab from free list.\n"); + return NULL; } list_head_insert_after(free_slab, &cachep->slabs_partial); } @@ -506,41 +628,50 @@ void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags) // Retrieve the slab page from the partial list. page_t *slab_page = list_entry(cachep->slabs_partial.next, page_t, slabs); if (!slab_page) { - pr_crit("We retrieved an invalid slab from the partial list."); - return NULL; // Return NULL to indicate failure. + pr_crit("Retrieved invalid slab from partial list.\n"); + return NULL; } // Allocate an object from the slab page. void *ptr = __kmem_cache_alloc_slab(cachep, slab_page); if (!ptr) { - pr_crit("We failed to allocate a slab."); - return NULL; // Return NULL to indicate failure. + pr_crit("Failed to allocate object from slab.\n"); + return NULL; } // If the slab is now full, move it to the full slabs list. if (slab_page->slab_objfree == 0) { list_head *slab_full_elem = list_head_pop(&cachep->slabs_partial); if (!slab_full_elem) { - pr_crit("We retrieved an invalid slab from the partial list."); - return NULL; // Return NULL to indicate failure. + pr_crit("Retrieved invalid slab from partial list while moving to full list.\n"); + return NULL; } list_head_insert_after(slab_full_elem, &cachep->slabs_full); } -#ifdef ENABLE_CACHE_TRACE - pr_notice("CACHE-ALLOC 0x%p in %-20s at %s:%d\n", ptr, cachep->name, file, line); +#if defined(ENABLE_CACHE_TRACE) || (__DEBUG_LEVEL__ >= LOGLEVEL_DEBUG) + pr_notice("kmem_cache_alloc 0x%p in %-20s at %s:%d\n", ptr, cachep->name, file, line); #endif + return ptr; // Return pointer to the allocated object. } -#ifdef ENABLE_CACHE_TRACE -void pr_kmem_cache_free(const char *file, const char *fun, int line, void *ptr) -#else -void kmem_cache_free(void *ptr) -#endif +int pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr) { + // Check for null pointer input + if (!addr) { + pr_crit("Null pointer provided.\n"); + return 1; + } + // Get the slab page corresponding to the given pointer. - page_t *slab_page = get_page_from_virtual_address((uint32_t)ptr); + page_t *slab_page = get_page_from_virtual_address((uint32_t)addr); + + // Check if slab_page retrieval was successful + if (!slab_page) { + pr_crit("Failed to get slab page for pointer 0x%p.\n", addr); + return 1; + } // If the slab main page is a low memory page, update to the root page. if (is_lowmem_page_struct(slab_page->container.slab_main_page)) { @@ -550,28 +681,42 @@ void kmem_cache_free(void *ptr) // Retrieve the cache pointer from the slab page. kmem_cache_t *cachep = slab_page->container.slab_cache; -#ifdef ENABLE_CACHE_TRACE - pr_notice("CACHE-FREE 0x%p in %-20s at %s:%d\n", ptr, cachep->name, file, line); + // Check if cachep retrieval was successful + if (!cachep) { + pr_crit("Failed to retrieve cache from slab page for pointer 0x%p.\n", addr); + return 1; + } + +#if defined(ENABLE_CACHE_TRACE) || (__DEBUG_LEVEL__ >= LOGLEVEL_DEBUG) + pr_notice("kmem_cache_free 0x%p in %-20s at %s:%d\n", addr, cachep->name, file, line); #endif + // Call the destructor if defined. if (cachep->dtor) { - cachep->dtor(ptr); + cachep->dtor(addr); } // Get the kmem_obj from the pointer. - kmem_obj_t *obj = KMEM_OBJ_FROM_ADDR(ptr); + kmem_obj_t *obj = KMEM_OBJ_FROM_ADDR(addr); + + // Check if obj retrieval was successful + if (!obj) { + pr_crit("Failed to retrieve kmem object for pointer 0x%p.\n", addr); + return 1; + } // Add object to the free list of the slab. list_head_insert_after(&obj->objlist, &slab_page->slab_freelist); slab_page->slab_objfree++; cachep->free_num++; - // If the slab is completely free, move it to the free list. + // Check if the slab is completely free, move it to the free list. if (slab_page->slab_objfree == slab_page->slab_objcnt) { // Remove the page from the partial list. list_head_remove(&slab_page->slabs); // Add the page to the free list. list_head_insert_after(&slab_page->slabs, &cachep->slabs_free); + pr_debug("Slab page 0x%p moved to free list.\n", slab_page); } // If the page is not full, update its list status. else if (slab_page->slab_objfree == 1) { @@ -579,14 +724,12 @@ void kmem_cache_free(void *ptr) list_head_remove(&slab_page->slabs); // Add the page to the partial list. list_head_insert_after(&slab_page->slabs, &cachep->slabs_partial); + pr_debug("Slab page 0x%p moved to partial list.\n", slab_page); } + return 0; } -#ifdef ENABLE_ALLOC_TRACE void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size) -#else -void *kmalloc(unsigned int size) -#endif { unsigned int order = 0; @@ -600,33 +743,53 @@ void *kmalloc(unsigned int size) void *ptr; if (order >= MAX_KMALLOC_CACHE_ORDER) { ptr = (void *)__alloc_pages_lowmem(GFP_KERNEL, order - 12); + if (!ptr) { + pr_crit("Failed to allocate raw pages for order %u at %s:%d\n", order, file, line); + } } else { ptr = kmem_cache_alloc(malloc_blocks[order], GFP_KERNEL); + if (!ptr) { + pr_crit("Failed to allocate from kmalloc cache order %u for size %u at %s:%d\n", order, size, file, line); + } } #ifdef ENABLE_ALLOC_TRACE - pr_notice("KMALLOC 0x%p at %s:%d\n", ptr, file, line); + if (ptr) { + pr_notice("kmalloc 0x%p of size %u at %s:%d\n", ptr, size, file, line); + } #endif - return ptr; // Return pointer to the allocated memory. + return ptr; } -#ifdef ENABLE_ALLOC_TRACE void pr_kfree(const char *file, const char *fun, int line, void *ptr) -#else -void kfree(void *ptr) -#endif { + if (!ptr) { + pr_warning("Attempt to free NULL pointer at %s:%d\n", file, line); + return; + } + #ifdef ENABLE_ALLOC_TRACE - pr_notice("KFREE 0x%p at %s:%d\n", ptr, file, line); + pr_notice("kfree 0x%p at %s:%d\n", ptr, file, line); #endif + // Get the slab page from the pointer's address. page_t *page = get_page_from_virtual_address((uint32_t)ptr); + // Check if page retrieval was successful. + if (!page) { + pr_crit("Failed to retrieve page for address 0x%p at %s:%d\n", ptr, file, line); + return; + } + // If the address belongs to a cache, free it using kmem_cache_free. if (page->container.slab_main_page) { - kmem_cache_free(ptr); + if (kmem_cache_free(ptr) < 0) { + pr_crit("Failed to free memory from kmem_cache for address 0x%p at %s:%d\n", ptr, file, line); + } } else { // Otherwise, free the raw pages. - free_pages_lowmem((uint32_t)ptr); + if (free_pages_lowmem((uint32_t)ptr) < 0) { + pr_crit("Failed to free raw pages for address 0x%p at %s:%d\n", ptr, file, line); + } } } diff --git a/mentos/src/mem/zone_allocator.c b/mentos/src/mem/zone_allocator.c index c37c1648..f07319ab 100644 --- a/mentos/src/mem/zone_allocator.c +++ b/mentos/src/mem/zone_allocator.c @@ -15,7 +15,7 @@ #include "mem/paging.h" #include "mem/zone_allocator.h" #include "string.h" -#include "sys/list_head.h" +#include "list_head.h" /// @brief Aligns the given address down to the nearest page boundary. /// @param addr The address to align. diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index 7f7eca5e..93e5443d 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -21,7 +21,7 @@ #include "process/scheduler.h" #include "process/wait.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/panic.h" #include "fs/vfs.h" #include "fs/namei.h" @@ -29,8 +29,6 @@ /// Cache for creating the task structs. static kmem_cache_t *task_struct_cache; -/// @brief The task_struct of the init process. -static task_struct *init_proc; /// @brief Counts the number of arguments. /// @param args the array of arguments, it must be NULL terminated. @@ -343,8 +341,12 @@ int init_tasking(void) task_struct *process_create_init(const char *path) { pr_debug("Building init process...\n"); + // Allocate the memory for the process. - init_proc = __alloc_task(NULL, NULL, "init"); + task_struct *init_proc = __alloc_task(NULL, NULL, "init"); + + // Active the current process. + scheduler_enqueue_task(init_proc); // == INITIALIZE `/proc/video` ============================================ // Check that the fd_list is initialized. @@ -418,14 +420,23 @@ task_struct *process_create_init(const char *path) paging_switch_directory(crtdir); // ------------------------------------------------------------------------ - // Active the current process. - scheduler_enqueue_task(init_proc); - pr_debug("Executing '%s' (pid: %d)...\n", init_proc->name, init_proc->pid); return init_proc; } +vfs_file_descriptor_t *fget(int fd) +{ + task_struct *current = scheduler_get_current_process(); + assert(current && "There is no current task running."); + // Check the current FD. + if (fd < 0 || fd >= current->max_fd) { + return NULL; + } + // Retrieve the file structure from the table. + return current->fd_list + fd; +} + char *sys_getcwd(char *buf, size_t size) { task_struct *current = scheduler_get_current_process(); diff --git a/mentos/src/process/scheduler.c b/mentos/src/process/scheduler.c index bc1c5144..a1b19b40 100644 --- a/mentos/src/process/scheduler.c +++ b/mentos/src/process/scheduler.c @@ -18,7 +18,7 @@ #include "process/scheduler_feedback.h" #include "process/wait.h" #include "strerror.h" -#include "sys/errno.h" +#include "errno.h" #include "system/panic.h" /// @brief Assembly function setting the kernel stack to jump into @@ -240,28 +240,36 @@ int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) wait_queue_entry_t *sleep_on(wait_queue_head_t *wq) { - // Save the sleeping process registers state + // Validate input parameters. + if (!wq) { + pr_err("sleep_on: Wait queue head is NULL.\n"); + return NULL; + } + + // Retrieve the current process/task. task_struct *sleeping_task = scheduler_get_current_process(); - // Stops task from runqueue making it unrunnable. + if (!sleeping_task) { + pr_err("sleep_on: Failed to retrieve the current process.\n"); + return NULL; + } + + // Set the task state to uninterruptible to indicate it is sleeping. sleeping_task->state = TASK_UNINTERRUPTIBLE; -#if 0 - // Get the interrupt registers. - pt_regs *f = get_current_interrupt_stack_frame(); - // Store its context. - scheduler_store_context(f, sleeping_task); - // Select next process in the runqueue as the current, restore it's context, - // we assume that the first process is init wich does not sleep (I hope). - // This is necessary to make the scheduler_run() in syscall_handler work. - task_struct *next = scheduler_pick_next_task(&runqueue); - assert((next != sleeping_task) && "The next selected process in the runqueue is the sleeping process"); - scheduler_restore_context(next, f); -#endif - // Allocate the wait_queue entry. + + // Allocate memory for a new wait queue entry. wait_queue_entry_t *wait_queue_entry = wait_queue_entry_alloc(); - // Initialize the entry. - init_waitqueue_entry(wait_queue_entry, sleeping_task); - // Add sleeping process to sleep wait queue. + if (!wait_queue_entry) { + pr_err("sleep_on: Failed to allocate memory for wait queue entry.\n"); + return NULL; + } + + // Initialize the wait queue entry with the current task. + wait_queue_entry_init(wait_queue_entry, sleeping_task); + + // Add the wait queue entry to the specified wait queue. add_wait_queue(wq, wait_queue_entry); + pr_debug("sleep_on: Added process %d to the wait queue.\n", sleeping_task->pid); + return wait_queue_entry; } diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index 32bed8d9..ae064093 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -15,7 +15,7 @@ #include "process/scheduler.h" #include "process/scheduler_feedback.h" #include "process/wait.h" -#include "sys/list_head.h" +#include "list_head.h" /// @brief Updates task execution statistics. /// @param task the task to update. diff --git a/mentos/src/process/wait.c b/mentos/src/process/wait.c index b0352d8c..45a0ab5e 100644 --- a/mentos/src/process/wait.c +++ b/mentos/src/process/wait.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[WAIT ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "process/wait.h" #include "mem/slab.h" @@ -30,6 +30,23 @@ static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_entry list_head_remove(&wq->task_list); } +void wait_queue_head_init(wait_queue_head_t *head) +{ + // Validate the queue pointer. + if (!head) { + pr_err("init_wait_queue_head: head is NULL.\n"); + return; + } + + // Initialize the spinlock for the wait queue. + spinlock_init(&head->lock); + + // Initialize the task list as an empty list. + list_head_init(&head->task_list); + + pr_debug("wait_queue_head_init: Initialized wait queue head at %p.\n", head); +} + wait_queue_entry_t *wait_queue_entry_alloc(void) { // Allocate the memory. @@ -40,9 +57,10 @@ wait_queue_entry_t *wait_queue_entry_alloc(void) // Clean the memory. memset(wait_queue_entry, 0, sizeof(wait_queue_entry_t)); // Initialize the element. - wait_queue_entry->flags = 0; - wait_queue_entry->task = NULL; - wait_queue_entry->func = NULL; + wait_queue_entry->flags = 0; + wait_queue_entry->task = NULL; + wait_queue_entry->func = NULL; + wait_queue_entry->private = NULL; list_head_init(&wait_queue_entry->task_list); // Return the element. return wait_queue_entry; @@ -56,7 +74,7 @@ void wait_queue_entry_dealloc(wait_queue_entry_t *wait_queue_entry) kfree(wait_queue_entry); } -void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task) +void wait_queue_entry_init(wait_queue_entry_t *wq, struct task_struct *task) { wq->flags = 0; wq->task = task; diff --git a/mentos/src/sys/utsname.c b/mentos/src/sys/utsname.c index 746441e4..3d535544 100644 --- a/mentos/src/sys/utsname.c +++ b/mentos/src/sys/utsname.c @@ -12,7 +12,7 @@ #include "fcntl.h" #include "fs/vfs.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/utsname.h" #include "version.h" diff --git a/mentos/src/system/errno.c b/mentos/src/system/errno.c index 44fd5b4d..12a2c24d 100644 --- a/mentos/src/system/errno.c +++ b/mentos/src/system/errno.c @@ -3,7 +3,7 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/errno.h" +#include "errno.h" #include "process/scheduler.h" /// @brief Returns the error number for the current process. diff --git a/mentos/src/system/printk.c b/mentos/src/system/printk.c index 804ca4b0..724af370 100644 --- a/mentos/src/system/printk.c +++ b/mentos/src/system/printk.c @@ -4,18 +4,9 @@ /// See LICENSE.md for details. #include "system/printk.h" -#include "io/video.h" -#include "stdarg.h" -#include "stdio.h" +#include "io/debug.h" -int sys_syslog(const char *format, ...) +void sys_syslog(const char *file, const char *fun, int line, short log_level, const char *format) { - char buffer[4096]; - va_list ap; - // Start variabile argument's list. - va_start(ap, format); - int len = vsprintf(buffer, format, ap); - va_end(ap); - video_puts(buffer); - return len; + dbg_printf(file, fun, line, "[SYSLOG]", log_level, format); } diff --git a/mentos/src/system/signal.c b/mentos/src/system/signal.c index bd25b4eb..c380ffe0 100644 --- a/mentos/src/system/signal.c +++ b/mentos/src/system/signal.c @@ -16,7 +16,7 @@ #include "process/scheduler.h" #include "process/wait.h" #include "string.h" -#include "sys/errno.h" +#include "errno.h" #include "system/signal.h" /// @brief Extracts the exit status. @@ -600,7 +600,8 @@ int signals_init(void) pr_emerg("Failed to allocate cache for signals.\n"); return 0; } - list_head_init(&stopped_queue.task_list); + // Initialize wait queue. + wait_queue_head_init(&stopped_queue); return 1; } @@ -757,8 +758,6 @@ sighandler_t sys_signal(int signum, sighandler_t handler, uint32_t sigreturn_add current_process->sigreturn_addr = sigreturn_addr; // Get the old sigaction. sigaction_t *old_sigaction = ¤t_process->sighand.action[signum - 1]; - pr_err("sys_signal(%d, %p): Signal action ptr %p\n", signum, handler, old_sigaction); - pr_err("sys_signal(%d, %p): Old signal handler %p\n", signum, handler, old_sigaction->sa_handler); // Get the old handler (to return). sighandler_t old_handler = current_process->sighand.action[signum - 1].sa_handler; // Set the new action. diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index 0b55a71e..e51a0c8f 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -15,33 +15,28 @@ #include "devices/fpu.h" #include "fs/attr.h" #include "fs/vfs.h" -#include "fs/ioctl.h" #include "hardware/timer.h" #include "kernel.h" #include "mem/kheap.h" #include "process/process.h" #include "process/scheduler.h" -#include "sys/errno.h" +#include "errno.h" #include "sys/mman.h" #include "sys/msg.h" #include "sys/sem.h" #include "sys/shm.h" #include "sys/utsname.h" #include "system/syscall.h" +#include "system/printk.h" /// The signature of a function call. typedef int (*SystemCall)(void); /// The signature of a function call. typedef int (*SystemCall5)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); -/// The signature of a function call. -typedef int (*SystemCall6)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); /// The list of function call. SystemCall sys_call_table[SYSCALL_NUMBER]; -/// Last interupt stack frame -static pt_regs *current_interrupt_stack_frame; - /// @brief A Not Implemented (NI) system-call. /// @return Always returns -ENOSYS. /// @details @@ -57,132 +52,120 @@ static inline int sys_ni_syscall(void) void syscall_init(void) { // Initialize the list of system calls. - sys_call_table[__NR_exit] = (SystemCall)sys_exit; - sys_call_table[__NR_fork] = (SystemCall)sys_fork; - sys_call_table[__NR_read] = (SystemCall)sys_read; - sys_call_table[__NR_write] = (SystemCall)sys_write; - sys_call_table[__NR_open] = (SystemCall)sys_open; - sys_call_table[__NR_close] = (SystemCall)sys_close; - sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; - sys_call_table[__NR_creat] = (SystemCall)sys_creat; - sys_call_table[__NR_unlink] = (SystemCall)sys_unlink; - sys_call_table[__NR_execve] = (SystemCall)sys_execve; - sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; - sys_call_table[__NR_time] = (SystemCall)sys_time; - sys_call_table[__NR_chmod] = (SystemCall)sys_chmod; - sys_call_table[__NR_lchown] = (SystemCall)sys_lchown; - sys_call_table[__NR_stat] = (SystemCall)sys_stat; - sys_call_table[__NR_lseek] = (SystemCall)sys_lseek; - sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; - sys_call_table[__NR_setuid] = (SystemCall)sys_setuid; - sys_call_table[__NR_getuid] = (SystemCall)sys_getuid; - sys_call_table[__NR_alarm] = (SystemCall)sys_alarm; - sys_call_table[__NR_fstat] = (SystemCall)sys_fstat; - sys_call_table[__NR_nice] = (SystemCall)sys_nice; - sys_call_table[__NR_kill] = (SystemCall)sys_kill; - sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; - sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; - sys_call_table[__NR_dup] = (SystemCall)sys_dup; - sys_call_table[__NR_brk] = (SystemCall)sys_brk; - sys_call_table[__NR_setgid] = (SystemCall)sys_setgid; - sys_call_table[__NR_getgid] = (SystemCall)sys_getgid; - sys_call_table[__NR_signal] = (SystemCall)sys_signal; - sys_call_table[__NR_geteuid] = (SystemCall)sys_geteuid; - sys_call_table[__NR_getegid] = (SystemCall)sys_getegid; - sys_call_table[__NR_ioctl] = (SystemCall)sys_ioctl; - sys_call_table[__NR_setpgid] = (SystemCall)sys_setpgid; - sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; - sys_call_table[__NR_setsid] = (SystemCall)sys_setsid; - sys_call_table[__NR_sigaction] = (SystemCall)sys_sigaction; - sys_call_table[__NR_setreuid] = (SystemCall)sys_setreuid; - sys_call_table[__NR_setregid] = (SystemCall)sys_setregid; - sys_call_table[__NR_symlink] = (SystemCall)sys_symlink; - sys_call_table[__NR_readlink] = (SystemCall)sys_readlink; - sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; - sys_call_table[__NR_mmap] = (SystemCall)sys_mmap; - sys_call_table[__NR_munmap] = (SystemCall)sys_munmap; - sys_call_table[__NR_fchmod] = (SystemCall)sys_fchmod; - sys_call_table[__NR_fchown] = (SystemCall)sys_fchown; - sys_call_table[__NR_setitimer] = (SystemCall)sys_setitimer; - sys_call_table[__NR_getitimer] = (SystemCall)sys_getitimer; - sys_call_table[__NR_uname] = (SystemCall)sys_uname; - sys_call_table[__NR_sigreturn] = (SystemCall)sys_sigreturn; - sys_call_table[__NR_sigprocmask] = (SystemCall)sys_sigprocmask; - sys_call_table[__NR_getpgid] = (SystemCall)sys_getpgid; - sys_call_table[__NR_fchdir] = (SystemCall)sys_fchdir; - sys_call_table[__NR_getdents] = (SystemCall)sys_getdents; - sys_call_table[__NR_getsid] = (SystemCall)sys_getsid; - sys_call_table[__NR_sched_setparam] = (SystemCall)sys_sched_setparam; - sys_call_table[__NR_sched_getparam] = (SystemCall)sys_sched_getparam; - sys_call_table[__NR_nanosleep] = (SystemCall)sys_nanosleep; - sys_call_table[__NR_chown] = (SystemCall)sys_chown; - sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; - sys_call_table[__NR_waitperiod] = (SystemCall)sys_waitperiod; - sys_call_table[__NR_msgctl] = (SystemCall)sys_msgctl; - sys_call_table[__NR_msgget] = (SystemCall)sys_msgget; - sys_call_table[__NR_msgrcv] = (SystemCall)sys_msgrcv; - sys_call_table[__NR_msgsnd] = (SystemCall)sys_msgsnd; - sys_call_table[__NR_semctl] = (SystemCall)sys_semctl; - sys_call_table[__NR_semget] = (SystemCall)sys_semget; - sys_call_table[__NR_semop] = (SystemCall)sys_semop; - sys_call_table[__NR_shmat] = (SystemCall)sys_shmat; - sys_call_table[__NR_shmctl] = (SystemCall)sys_shmctl; - sys_call_table[__NR_shmdt] = (SystemCall)sys_shmdt; - sys_call_table[__NR_shmget] = (SystemCall)sys_shmget; + sys_call_table[__NR_exit] = (SystemCall)sys_exit; + sys_call_table[__NR_fork] = (SystemCall)sys_fork; + sys_call_table[__NR_read] = (SystemCall)sys_read; + sys_call_table[__NR_write] = (SystemCall)sys_write; + sys_call_table[__NR_open] = (SystemCall)sys_open; + sys_call_table[__NR_close] = (SystemCall)sys_close; + sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; + sys_call_table[__NR_creat] = (SystemCall)sys_creat; + sys_call_table[__NR_unlink] = (SystemCall)sys_unlink; + sys_call_table[__NR_execve] = (SystemCall)sys_execve; + sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; + sys_call_table[__NR_time] = (SystemCall)sys_time; + sys_call_table[__NR_chmod] = (SystemCall)sys_chmod; + sys_call_table[__NR_lchown] = (SystemCall)sys_lchown; + sys_call_table[__NR_stat] = (SystemCall)sys_stat; + sys_call_table[__NR_lseek] = (SystemCall)sys_lseek; + sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; + sys_call_table[__NR_setuid] = (SystemCall)sys_setuid; + sys_call_table[__NR_getuid] = (SystemCall)sys_getuid; + sys_call_table[__NR_alarm] = (SystemCall)sys_alarm; + sys_call_table[__NR_fstat] = (SystemCall)sys_fstat; + sys_call_table[__NR_nice] = (SystemCall)sys_nice; + sys_call_table[__NR_kill] = (SystemCall)sys_kill; + sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; + sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; + sys_call_table[__NR_dup] = (SystemCall)sys_dup; + sys_call_table[__NR_pipe] = (SystemCall)sys_pipe; + sys_call_table[__NR_brk] = (SystemCall)sys_brk; + sys_call_table[__NR_setgid] = (SystemCall)sys_setgid; + sys_call_table[__NR_getgid] = (SystemCall)sys_getgid; + sys_call_table[__NR_signal] = (SystemCall)sys_signal; + sys_call_table[__NR_geteuid] = (SystemCall)sys_geteuid; + sys_call_table[__NR_getegid] = (SystemCall)sys_getegid; + sys_call_table[__NR_ioctl] = (SystemCall)sys_ioctl; + sys_call_table[__NR_fcntl] = (SystemCall)sys_fcntl; + sys_call_table[__NR_setpgid] = (SystemCall)sys_setpgid; + sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; + sys_call_table[__NR_setsid] = (SystemCall)sys_setsid; + sys_call_table[__NR_sigaction] = (SystemCall)sys_sigaction; + sys_call_table[__NR_setreuid] = (SystemCall)sys_setreuid; + sys_call_table[__NR_setregid] = (SystemCall)sys_setregid; + sys_call_table[__NR_symlink] = (SystemCall)sys_symlink; + sys_call_table[__NR_readlink] = (SystemCall)sys_readlink; + sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; + sys_call_table[__NR_mmap] = (SystemCall)sys_mmap; + sys_call_table[__NR_munmap] = (SystemCall)sys_munmap; + sys_call_table[__NR_syslog] = (SystemCall)sys_syslog; + sys_call_table[__NR_fchmod] = (SystemCall)sys_fchmod; + sys_call_table[__NR_fchown] = (SystemCall)sys_fchown; + sys_call_table[__NR_setitimer] = (SystemCall)sys_setitimer; + sys_call_table[__NR_getitimer] = (SystemCall)sys_getitimer; + sys_call_table[__NR_uname] = (SystemCall)sys_uname; + sys_call_table[__NR_sigreturn] = (SystemCall)sys_sigreturn; + sys_call_table[__NR_sigprocmask] = (SystemCall)sys_sigprocmask; + sys_call_table[__NR_getpgid] = (SystemCall)sys_getpgid; + sys_call_table[__NR_fchdir] = (SystemCall)sys_fchdir; + sys_call_table[__NR_getdents] = (SystemCall)sys_getdents; + sys_call_table[__NR_getsid] = (SystemCall)sys_getsid; + sys_call_table[__NR_sched_setparam] = (SystemCall)sys_sched_setparam; + sys_call_table[__NR_sched_getparam] = (SystemCall)sys_sched_getparam; + sys_call_table[__NR_nanosleep] = (SystemCall)sys_nanosleep; + sys_call_table[__NR_chown] = (SystemCall)sys_chown; + sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; + sys_call_table[__NR_waitperiod] = (SystemCall)sys_waitperiod; + sys_call_table[__NR_msgctl] = (SystemCall)sys_msgctl; + sys_call_table[__NR_msgget] = (SystemCall)sys_msgget; + sys_call_table[__NR_msgrcv] = (SystemCall)sys_msgrcv; + sys_call_table[__NR_msgsnd] = (SystemCall)sys_msgsnd; + sys_call_table[__NR_semctl] = (SystemCall)sys_semctl; + sys_call_table[__NR_semget] = (SystemCall)sys_semget; + sys_call_table[__NR_semop] = (SystemCall)sys_semop; + sys_call_table[__NR_shmat] = (SystemCall)sys_shmat; + sys_call_table[__NR_shmctl] = (SystemCall)sys_shmctl; + sys_call_table[__NR_shmdt] = (SystemCall)sys_shmdt; + sys_call_table[__NR_shmget] = (SystemCall)sys_shmget; isr_install_handler(SYSTEM_CALL, &syscall_handler, "syscall_handler"); } -pt_regs *get_current_interrupt_stack_frame(void) -{ - return current_interrupt_stack_frame; -} - void syscall_handler(pt_regs *f) { - // Saves current interrupt stack frame - current_interrupt_stack_frame = f; - - // dbg_print_regs(f); // Save current process fpu state. switch_fpu(); - // The index of the requested system call. - uint32_t sc_index = f->eax; - // The result of the system call. - int ret; - if (sc_index >= SYSCALL_NUMBER) { - ret = ENOSYS; + if (f->eax >= SYSCALL_NUMBER) { + f->eax = ENOSYS; } else { - uintptr_t ptr = (uintptr_t)sys_call_table[sc_index]; + // Retrieve the system call function from the system call table. + SystemCall5 fun = (SystemCall5)sys_call_table[f->eax]; - uint32_t arg0 = f->ebx; - uint32_t arg1 = f->ecx; - uint32_t arg2 = f->edx; - uint32_t arg3 = f->esi; - uint32_t arg4 = f->edi; - if ((sc_index == __NR_fork) || - (sc_index == __NR_clone) || - (sc_index == __NR_execve) || - (sc_index == __NR_sigreturn)) { - arg0 = (uintptr_t)f; + // Initialize an array to hold up to 5 arguments for the system call. + unsigned args[5] = { 0 }; + + // Special handling for specific system calls that do not follow the standard argument convention. + if ((f->eax == __NR_fork) || (f->eax == __NR_clone) || (f->eax == __NR_execve) || (f->eax == __NR_sigreturn)) { + args[0] = (uintptr_t)f; } - if (sc_index == __NR_mmap) { - SystemCall6 func = (SystemCall6)ptr; - // Get the arguments. - unsigned *args = (unsigned *)arg0; - // Call the function. - ret = func(args[0], args[1], args[2], args[3], args[4], args[5]); - } else { - SystemCall5 func = (SystemCall5)ptr; - ret = func(arg0, arg1, arg2, arg3, arg4); + // Otherwise, populate arguments from the CPU register state. + else { + args[0] = f->ebx; + args[1] = f->ecx; + args[2] = f->edx; + args[3] = f->esi; + args[4] = f->edi; } + + // Invoke the system call with the prepared arguments and store the return value in the EAX register. + f->eax = fun(args[0], args[1], args[2], args[3], args[4]); } - f->eax = ret; // Schedule next process. scheduler_run(f); + // Restore fpu state. unswitch_fpu(); } diff --git a/programs/cat.c b/programs/cat.c index eda13ad9..f5f5b4bb 100644 --- a/programs/cat.c +++ b/programs/cat.c @@ -3,7 +3,6 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "io/debug.h" #include "stddef.h" #include #include diff --git a/programs/chmod.c b/programs/chmod.c index 0a745e9f..e72f124d 100644 --- a/programs/chmod.c +++ b/programs/chmod.c @@ -3,7 +3,7 @@ /// @copyright (c) 2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include +#include #include #include #include diff --git a/programs/chown.c b/programs/chown.c index 069f0c9e..f23f290c 100644 --- a/programs/chown.c +++ b/programs/chown.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include diff --git a/programs/edit.c b/programs/edit.c index caa9c988..a1f217be 100644 --- a/programs/edit.c +++ b/programs/edit.c @@ -3,12 +3,6 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[EDIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include #include #include @@ -342,12 +336,9 @@ void edit_file(char *lines, size_t bufsize, int num_lines, const char *filename, // Handle arrow keys and escape sequences. if (c == '\033') { - pr_debug("[ ](%d)\n", c); c = getchar(); - pr_debug("[%c](%d)\n", c, c); if (c == '[') { c = getchar(); - pr_debug("[%c](%d)\n", c, c); // UP Arrow. if (c == 'A') { if (cy > 0) { @@ -436,15 +427,12 @@ void edit_file(char *lines, size_t bufsize, int num_lines, const char *filename, // Handle Ctrl + Arrows. else if (c == '1') { c = getchar(); - pr_debug("[%c](%d)\n", c, c); // CTRL + ARROW if (c == ';') { c = getchar(); - pr_debug("[%c](%d)\n", c, c); if (c == '5') { c = getchar(); - pr_debug("[%c](%d)\n", c, c); // Handle Ctrl+Right Arrow (Move to the beginning of the next word). if (c == 'C') { // Skip spaces first diff --git a/programs/kill.c b/programs/kill.c index 7ff16957..b0b2f6a4 100644 --- a/programs/kill.c +++ b/programs/kill.c @@ -9,7 +9,6 @@ #include #include #include -#include static inline int is_number(char *s) { diff --git a/programs/login.c b/programs/login.c index 557e1a3e..386b8290 100644 --- a/programs/login.c +++ b/programs/login.c @@ -3,12 +3,6 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[LOGIN ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include #include #include @@ -78,8 +72,6 @@ static inline int __read_input(char *buffer, size_t size, int show) do { c = getchar(); // Read a character from input - pr_debug("%c\n", c); - // Ignore EOF and null or tab characters if (c == EOF || c == 0 || c == '\t') { continue; diff --git a/programs/ls.c b/programs/ls.c index 4b3a6879..4e3b8f90 100644 --- a/programs/ls.c +++ b/programs/ls.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #define FLAG_L (1U << 0U) @@ -24,6 +23,22 @@ #define DENTS_NUM 12 +static inline const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) { + dblBytes = bytes / 1024.0; + } + } + sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); + return output; +} + static inline void print_dir_entry_name(const char *name, mode_t st_mode) { if (S_ISSOCK(st_mode)) { diff --git a/programs/runtests.c b/programs/runtests.c index ca6b2574..d16290a5 100644 --- a/programs/runtests.c +++ b/programs/runtests.c @@ -10,66 +10,60 @@ #include #include #include -#include +#include #include #include #include - -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[TRUNS ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include #define SHUTDOWN_PORT 0x604 /// Second serial port for QEMU. #define SERIAL_COM2 0x02F8 static char *all_tests[] = { - "t_exit", "t_abort", "t_alarm", + // "t_big_write", "t_chdir", - "t_time", - /* "t_big_write", */ "t_creat", "t_dup", - "t_exec execl", - "t_exec execlp", - "t_exec execle", - "t_exec execlpe", - "t_exec execv", - "t_exec execvp", - "t_exec execve", - "t_exec execvpe", - "t_fork 10", + "t_environ", + "t_exit", + "t_exec", + "t_fork", "t_gid", "t_grp", "t_groups", + "t_hashmap", "t_itimer", "t_kill", - /* "t_mem", */ + "t_list", + "t_mem", "t_mkdir", "t_msgget", - /* "t_periodic1", */ - /* "t_periodic2", */ - /* "t_periodic3", */ + "t_ndtree", + // "t_periodic1", + // "t_periodic2", + // "t_periodic3", + "t_pipe_blocking", + "t_pipe_non_blocking", "t_pwd", "t_schedfb", "t_semflg", "t_semget", "t_semop", - "t_setenv", + "t_shm", "t_shmget", - /* "t_shm_read", */ - /* "t_shm_write", */ "t_sigaction", "t_sigfpe", "t_siginfo", "t_sigmask", "t_sigusr", "t_sleep", + "t_spwd", "t_stopcont", + "t_syslog", + "t_time", "t_write_read", }; @@ -227,7 +221,7 @@ int runtests_main(int argc, char **argv) char *test_argv[32]; for (int i = 0; i < testsc; i++) { - pr_info("Running test (%2d/%2d): %s\n", i + 1, testsc, tests[i]); + syslog(LOG_INFO, "Running test (%2d/%2d): %s\n", i + 1, testsc, tests[i]); run_test(i + 1, tests[i]); } diff --git a/programs/shell.c b/programs/shell.c index 32c50f4b..110f0379 100644 --- a/programs/shell.c +++ b/programs/shell.c @@ -3,19 +3,12 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SHELL ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. - #include #include #include #include #include #include -#include #include #include #include @@ -85,7 +78,7 @@ static inline int __count_words(const char *sentence) { // Check if the input sentence is valid. if (sentence == NULL) { - pr_crit("__count_words: Invalid input, sentence is NULL.\n"); + fprintf(stderr, "__count_words: Invalid input, sentence is NULL.\n"); return -1; // Return -1 to indicate an error. } int result = 0; @@ -122,13 +115,11 @@ static inline int __folder_contains( { // Validate input parameters. if ((folder == NULL) || (entry == NULL) || (result == NULL)) { - pr_crit("__folder_contains: Invalid input parameters.\n"); return 0; // Return 0 to indicate an error. } // Attempt to open the folder with read-only and directory flags. int fd = open(folder, O_RDONLY | O_DIRECTORY, 0); if (fd == -1) { - pr_crit("__folder_contains: Failed to open folder: %s\n", folder); return 0; // Return 0 if the folder couldn't be opened. } // Prepare variables for the search. @@ -138,7 +129,6 @@ static inline int __folder_contains( // Calculate the length of the entry name. entry_len = strlen(entry); if (entry_len == 0) { - pr_crit("__folder_contains: Invalid entry name (empty).\n"); close(fd); // Close the folder before returning. return 0; // Return 0 if the entry name is empty. } @@ -170,7 +160,7 @@ static inline int __search_in_path(const char *entry, dirent_t *result) { // Validate input parameters. if ((entry == NULL) || (result == NULL)) { - pr_crit("__search_in_path: Invalid input parameters.\n"); + fprintf(stderr, "__search_in_path: Invalid input parameters.\n"); return 0; // Return 0 to indicate an error. } // Retrieve the PATH environment variable. @@ -199,7 +189,7 @@ static inline void __prompt_print(void) // Get the current working directory. char CWD[PATH_MAX]; if (getcwd(CWD, PATH_MAX) == NULL) { - pr_crit("__prompt_print: Failed to get current working directory.\n"); + fprintf(stderr, "__prompt_print: Failed to get current working directory.\n"); strcpy(CWD, "error"); } // Get the HOME environment variable. @@ -213,19 +203,19 @@ static inline void __prompt_print(void) // Get the USER environment variable. char *USER = getenv("USER"); if (USER == NULL) { - pr_crit("__prompt_print: Failed to get USER environment variable.\n"); + fprintf(stderr, "__prompt_print: Failed to get USER environment variable.\n"); USER = "error"; } // Get the current time. time_t rawtime = time(NULL); if (rawtime == (time_t)(-1)) { - pr_crit("__prompt_print: Failed to get current time.\n"); + fprintf(stderr, "__prompt_print: Failed to get current time.\n"); rawtime = 0; // Set to 0 in case of failure. } // Convert time to local time format. tm_t *timeinfo = localtime(&rawtime); if (timeinfo == NULL) { - pr_crit("__prompt_print: Failed to convert time to local time.\n"); + fprintf(stderr, "__prompt_print: Failed to convert time to local time.\n"); // Use a default value to avoid segmentation faults. static tm_t default_time = { 0 }; timeinfo = &default_time; @@ -234,7 +224,7 @@ static inline void __prompt_print(void) char *HOSTNAME; utsname_t buffer; if (uname(&buffer) < 0) { - pr_crit("__prompt_print: Failed to get hostname using uname.\n"); + fprintf(stderr, "__prompt_print: Failed to get hostname using uname.\n"); HOSTNAME = "error"; } else { HOSTNAME = buffer.nodename; @@ -284,7 +274,7 @@ static void ___expand_env(char *str, size_t str_len, char *buf, size_t buf_len, { // Input validation: Ensure that str and buf are not NULL, and that buf_len is valid. if ((str == NULL) || (buf == NULL) || (buf_len == 0)) { - pr_crit("Error: Invalid input parameters to ___expand_env.\n"); + fprintf(stderr, "Error: Invalid input parameters to ___expand_env.\n"); return; } // Buffer where we store the name of the variable. @@ -526,18 +516,6 @@ static int __cd(int argc, char *argv[]) return 0; } -void __history_print(void) -{ - rb_history_entry_t entry; - rb_history_init_entry(&entry); - pr_notice("H[S:%2u, C:%2u] :\n", history.size, history.count); - for (unsigned i = 0; i < history.count; i++) { - rb_history_get(&history, i, &entry); - pr_notice("[%2u] %s\n", i, entry.buffer); - } - pr_notice("\n"); -} - /// @brief Push the command inside the history. /// @param entry The history entry to be added. /// @return Returns 1 if the entry was successfully added, 0 if it was a duplicate. @@ -546,7 +524,7 @@ static inline int __history_push(rb_history_entry_t *entry) rb_history_entry_t previous_entry; // Validate input parameter. if (entry == NULL) { - pr_crit("__history_push: Invalid entry.\n"); + fprintf(stderr, "__history_push: Invalid entry.\n"); return 0; } // Check if there's an existing entry at the back of the history. @@ -602,12 +580,12 @@ static inline int __command_append( { // Input validation: Ensure entry, index, and length are valid. if ((entry == NULL) || (index == NULL) || (length == NULL)) { - pr_crit("Error: Invalid input to __command_append.\n"); + fprintf(stderr, "Error: Invalid input to __command_append.\n"); return 1; } // Ensure index does not exceed the buffer size limit. if ((*index) >= entry->size) { - pr_crit("Error: Index exceeds buffer size.\n"); + fprintf(stderr, "Error: Index exceeds buffer size.\n"); return 1; } // Insert the new character at the current index in the buffer, then @@ -636,13 +614,13 @@ static inline void __command_clear(rb_history_entry_t *entry, int *index, int *l { // Validate the input parameters to avoid null pointer dereference. if ((entry == NULL) || (index == NULL) || (length == NULL)) { - pr_crit("Invalid parameters passed to __command_clear.\n"); + fprintf(stderr, "Invalid parameters passed to __command_clear.\n"); return; } // Ensure index and length are greater than zero and index does not exceed // length. if ((*index < 0) || (*length < 0) || (*index > *length)) { - pr_crit("Invalid index or length values: index=%d, length=%d.\n", *index, *length); + fprintf(stderr, "shell: Invalid index or length values: index=%d, length=%d.\n", *index, *length); return; } // Move the cursor to the end of the current command. @@ -710,7 +688,19 @@ static int __command_complete( int *index, int *length) { - pr_debug("__command_complete(%s, %2d, %2d)\n", entry->buffer, *index, *length); + // Validate input parameters. + if (entry == NULL) { + fprintf(stderr, "__command_complete: 'entry' parameter is NULL.\n"); + return 0; + } + if (index == NULL) { + fprintf(stderr, "__command_complete: 'index' parameter is NULL.\n"); + return 0; + } + if (length == NULL) { + fprintf(stderr, "__command_complete: 'length' parameter is NULL.\n"); + return 0; + } char cwd[PATH_MAX]; // Buffer to store the current working directory. int words; // Variable to store the word count. @@ -721,22 +711,19 @@ static int __command_complete( // If there are no words in the command buffer, log it and return. if (words == 0) { - pr_debug("__command_complete(%s, %2d, %2d) : No words.\n", entry->buffer, *index, *length); return 0; } // Determines if we are at the beginning of a new argument, last character is space. if (__is_separator(entry->buffer[(*index) - 1])) { - pr_debug("__command_complete(%s, %2d, %2d) : Separator.\n", entry->buffer, *index, *length); return 0; } // If the last two characters are two dots `..` append a slash `/`, and // continue. if (((*index) >= 2) && ((entry->buffer[(*index) - 1] == '.') && (entry->buffer[(*index) - 2] == '.'))) { - pr_debug("__command_complete(%s, %2d, %2d) : Append '/'.\n", entry->buffer, *index, *length); if (__command_append(entry, index, length, '/')) { - pr_crit("Failed to append character.\n"); + fprintf(stderr, "Failed to append character.\n"); return 1; } } @@ -744,7 +731,7 @@ static int __command_complete( // Attempt to retrieve the current working directory. if (getcwd(cwd, PATH_MAX) == NULL) { // Error handling: If getcwd fails, it returns NULL - pr_crit("Failed to get current working directory.\n"); + fprintf(stderr, "Failed to get current working directory.\n"); return 1; } @@ -756,8 +743,6 @@ static int __command_complete( // If there is only one word, we are searching for a command. if (is_run_cmd) { if (__folder_contains(cwd, entry->buffer + 2, 0, &dent)) { - pr_debug("__command_complete(%s, %2d, %2d) : Suggest run '%s' -> '%s'.\n", entry->buffer, *index, *length, - entry->buffer + 2, dent.d_name); __command_suggest( dent.d_name, dent.d_type, @@ -779,8 +764,6 @@ static int __command_complete( return 0; } if (__folder_contains(_dirname, _basename, 0, &dent)) { - pr_debug("__command_complete(%s, %2d, %2d) : Suggest abs '%s' -> '%s'.\n", entry->buffer, *index, *length, - entry->buffer, dent.d_name); __command_suggest( dent.d_name, dent.d_type, @@ -791,8 +774,6 @@ static int __command_complete( } } else if (words == 1) { if (__search_in_path(entry->buffer, &dent)) { - pr_debug("__command_complete(%s, %2d, %2d) : Suggest in path '%s' -> '%s'.\n", entry->buffer, *index, *length, - entry->buffer, dent.d_name); __command_suggest( dent.d_name, dent.d_type, @@ -809,25 +790,21 @@ static int __command_complete( last_argument = last_argument ? last_argument + 1 : NULL; // If there is no last argument. if (last_argument == NULL) { - pr_crit("__command_complete: No last argument found in buffer '%s'.\n", entry->buffer); + fprintf(stderr, "__command_complete: No last argument found in buffer '%s'.\n", entry->buffer); return 0; } char _dirname[PATH_MAX]; if (!dirname(last_argument, _dirname, sizeof(_dirname))) { - pr_crit("__command_complete: Failed to extract directory name from '%s'.\n", last_argument); + fprintf(stderr, "__command_complete: Failed to extract directory name from '%s'.\n", last_argument); return 0; } const char *_basename = basename(last_argument); if (!_basename) { - pr_crit("__command_complete: Failed to extract basename from '%s'.\n", last_argument); + fprintf(stderr, "__command_complete: Failed to extract basename from '%s'.\n", last_argument); return 0; } - pr_debug("__command_complete(%s, %2d, %2d) : dirname='%s', basename='%s', last_argument='%s'.\n", entry->buffer, *index, *length, - _dirname, _basename, last_argument); if ((*_dirname != 0) && (*_basename != 0)) { if (__folder_contains(_dirname, _basename, 0, &dent)) { - pr_debug("__command_complete(%s, %2d, %2d) : Suggest 1 '%s' -> '%s'.\n", entry->buffer, *index, *length, - last_argument, dent.d_name); __command_suggest( dent.d_name, dent.d_type, @@ -838,8 +815,6 @@ static int __command_complete( } } else if (*_basename != 0) { if (__folder_contains(cwd, _basename, 0, &dent)) { - pr_debug("__command_complete(%s, %2d, %2d) : Suggest 2 '%s' -> '%s'.\n", entry->buffer, *index, *length, - last_argument, dent.d_name); __command_suggest( dent.d_name, dent.d_type, @@ -951,7 +926,6 @@ static inline int __read_command(rb_history_entry_t *entry) } // LEFT ARROW else if (c == 'D') { - pr_debug("%d > 0\n", index); if (index > 0) { puts("\033[1D"); // Move the cursor left index--; // Decrease index @@ -959,7 +933,6 @@ static inline int __read_command(rb_history_entry_t *entry) } // RIGHT ARROW else if (c == 'C') { - pr_debug("%d < %d\n", index, length); if (index < length) { puts("\033[1C"); // Move the cursor right index++; // Increase index @@ -1382,7 +1355,7 @@ static void __interactive_mode(void) // Get the input command. if (__read_command(&entry) < 0) { - pr_crit("Error reading command...\n"); + fprintf(stderr, "Error reading command...\n"); // Restore terminal attributes tcgetattr(STDIN_FILENO, &_termios); _termios.c_lflag |= (ICANON | ECHO | ISIG); // Re-enable canonical mode and echo diff --git a/programs/sleep.c b/programs/sleep.c index cd6735b4..c2e55ffc 100644 --- a/programs/sleep.c +++ b/programs/sleep.c @@ -1,18 +1,32 @@ /// @file sleep.c -/// @brief +/// @brief Simple sleep program that pauses for a specified number of seconds. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include #include -#include +#include int main(int argc, char **argv) { + // Check if an argument is provided if (argc == 2) { + // Convert the argument to an integer int amount = atoi(argv[1]); + + // Ensure the amount is positive if (amount > 0) { + printf("Sleeping for %d seconds...\n", amount); sleep(amount); + printf("Awake after %d seconds.\n", amount); + } else { + fprintf(stderr, "Error: Please enter a positive integer for sleep duration.\n"); + return 1; } + } else { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; } + return 0; } diff --git a/programs/stat.c b/programs/stat.c index be3bca22..83b9e926 100644 --- a/programs/stat.c +++ b/programs/stat.c @@ -4,19 +4,34 @@ /// See LICENSE.md for details. #include -#include #include #include #include #include #include #include -#include +#include #include #include #include #include +static inline const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) { + dblBytes = bytes / 1024.0; + } + } + sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); + return output; +} + static void __print_time(const char *prefix, time_t *time) { tm_t *timeinfo = localtime(time); diff --git a/programs/tests/CMakeLists.txt b/programs/tests/CMakeLists.txt index a5ae747c..861b202a 100644 --- a/programs/tests/CMakeLists.txt +++ b/programs/tests/CMakeLists.txt @@ -12,16 +12,17 @@ set(TEST_LIST t_gid.c t_alarm.c t_periodic3.c - t_setenv.c + t_environ.c t_itimer.c t_fork.c t_semget.c t_exec.c t_sleep.c t_periodic2.c + t_pipe_blocking.c + t_pipe_non_blocking.c t_sigfpe.c t_sigmask.c - t_getenv.c t_sigusr.c t_shmget.c t_stopcont.c @@ -35,11 +36,14 @@ set(TEST_LIST t_msgget.c t_periodic1.c t_kill.c - t_shm_write.c + t_shm.c t_mem.c - t_shm_read.c t_spwd.c t_big_write.c + t_syslog.c + t_ndtree.c + t_list.c + t_hashmap.c ) # Set the directory where the compiled binaries will be placed. diff --git a/programs/tests/t_alarm.c b/programs/tests/t_alarm.c index b59a18a9..7f01950e 100644 --- a/programs/tests/t_alarm.c +++ b/programs/tests/t_alarm.c @@ -18,21 +18,21 @@ void alarm_handler(int sig) { printf("handler(%d) : Starting handler.\n", sig); if (sig == SIGALRM) { - // Set an alarm to go off after 5 seconds. - alarm(5); + // Set an alarm to go off after 1 seconds. + alarm(1); - // Set another alarm to go off after 5 seconds and get the remaining time of the previous alarm. - int rest = alarm(5); + // Set another alarm to go off after 1 seconds and get the remaining time of the previous alarm. + int rest = alarm(1); - // Expected value: 5 (since the previous alarm was just set to 5 seconds). - printf("handler(%d) : alarm(5) result: %d.\n", sig, rest); + // Expected value: 1 (since the previous alarm was just set to 1 seconds). + printf("handler(%d) : alarm(1) result: %d.\n", sig, rest); // Cancel the alarm and get the remaining time of the previous alarm. rest = alarm(0); - // Expected value: ~4 (since the previous alarm was just set to 5 + // Expected value: ~4 (since the previous alarm was just set to 1 // seconds again). This small delay between the two alarm calls is why - // you see the value 4 instead of 5. The exact value can vary slightly + // you see the value 4 instead of 1. The exact value can vary slightly // depending on the system’s execution speed and the time taken to // execute the intermediate code. printf("handler(%d) : alarm(0) result: %d.\n", sig, rest); @@ -56,8 +56,8 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - // Set an alarm to go off after 5 seconds. - alarm(5); + // Set an alarm to go off after 1 seconds. + alarm(1); // Infinite loop to keep the program running until the alarm signal is received. while (1) {} diff --git a/programs/tests/t_big_write.c b/programs/tests/t_big_write.c index fb1e9d27..19ce7d31 100644 --- a/programs/tests/t_big_write.c +++ b/programs/tests/t_big_write.c @@ -13,38 +13,31 @@ #include #include +#define FILENAME "/home/user/test.txt" +#define ITERATIONS 8 +#define BUFFER_SIZE BUFSIZ + int main(int argc, char *argv[]) { - int flags = O_WRONLY | O_CREAT | O_TRUNC; - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; - char *filename = "/home/user/test.txt"; - char buffer[4 * BUFSIZ] = { 0 }; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + char write_buffer[BUFFER_SIZE] = { 0 }; + char read_buffer[BUFFER_SIZE] = { 0 }; // Open the file with specified flags and mode. - int fd = open(filename, flags, mode); + int fd = open(FILENAME, O_WRONLY | O_CREAT | O_TRUNC, mode); if (fd < 0) { - // Error handling for file open failure. - fprintf(STDERR_FILENO, "Failed to open file %s: %s\n", filename, strerror(errno)); + fprintf(stderr, "Failed to open file %s: %s\n", FILENAME, strerror(errno)); return EXIT_FAILURE; } - // Write the content to the file. - for (unsigned times = 0; times < 128; ++times) { + // Write test data to the file. + for (unsigned times = 0; times < ITERATIONS; ++times) { for (unsigned i = 'A'; i < 'z'; ++i) { - // Fill the buffer with the character 'i'. - memset(buffer, i, 4 * BUFSIZ); - // Write the buffer to the file. - if (write(fd, buffer, 4 * BUFSIZ) < 0) { - // Error handling for write failure. - fprintf(STDERR_FILENO, "Writing to file %s failed: %s\n", filename, strerror(errno)); - // Close the file descriptor. - if (close(fd) < 0) { - fprintf(STDERR_FILENO, "Failed to close file %s: %s\n", filename, strerror(errno)); - } - // Delete the file. - if (unlink(filename) < 0) { - fprintf(STDERR_FILENO, "Failed to delete file %s: %s\n", filename, strerror(errno)); - } + memset(write_buffer, i, sizeof(write_buffer)); + if (write(fd, write_buffer, sizeof(write_buffer)) < 0) { + fprintf(stderr, "Writing to file %s failed: %s\n", FILENAME, strerror(errno)); + close(fd); + unlink(FILENAME); return EXIT_FAILURE; } } @@ -52,12 +45,50 @@ int main(int argc, char *argv[]) // Close the file descriptor. if (close(fd) < 0) { - fprintf(STDERR_FILENO, "Failed to close file %s: %s\n", filename, strerror(errno)); + fprintf(stderr, "Failed to close file %s: %s\n", FILENAME, strerror(errno)); + unlink(FILENAME); + return EXIT_FAILURE; + } + + // Open the file with specified flags and mode. + fd = open(FILENAME, O_RDONLY, mode); + if (fd < 0) { + fprintf(stderr, "Failed to open file %s: %s\n", FILENAME, strerror(errno)); + unlink(FILENAME); return EXIT_FAILURE; } - // Delete the file. - if (unlink(filename) < 0) { - fprintf(STDERR_FILENO, "Failed to delete file %s: %s\n", filename, strerror(errno)); + + // Read and verify data from the file. + for (unsigned times = 0; times < ITERATIONS; ++times) { + for (unsigned i = 'A'; i < 'z'; ++i) { + memset(write_buffer, i, sizeof(write_buffer)); + if (read(fd, read_buffer, sizeof(read_buffer)) < 0) { + fprintf(stderr, "Reading from file %s failed: %s\n", FILENAME, strerror(errno)); + close(fd); + unlink(FILENAME); + return EXIT_FAILURE; + } + + // Verify read data matches what was written. + if (memcmp(write_buffer, read_buffer, sizeof(write_buffer)) != 0) { + fprintf(stderr, "Data mismatch in file %s at iteration %u, char %c\n", FILENAME, times, i); + close(fd); + unlink(FILENAME); + return EXIT_FAILURE; + } + } + } + + // Close the file descriptor. + if (close(fd) < 0) { + fprintf(stderr, "Failed to close file %s: %s\n", FILENAME, strerror(errno)); + unlink(FILENAME); + return EXIT_FAILURE; + } + + // Delete the test file. + if (unlink(FILENAME) < 0) { + fprintf(stderr, "Failed to delete file %s: %s\n", FILENAME, strerror(errno)); return EXIT_FAILURE; } diff --git a/programs/tests/t_environ.c b/programs/tests/t_environ.c new file mode 100644 index 00000000..6ec58cdc --- /dev/null +++ b/programs/tests/t_environ.c @@ -0,0 +1,63 @@ +/// @file t_getenv.c +/// @brief Test the getenv function. +/// @details This program tests the `getenv` function by retrieving the value of +/// an environment variable and printing it. If the environment variable is not +/// set, it prints an error message and exits with a failure status. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + const char *env_var = "TEST_ENV"; + const char *initial_value = "InitialValue"; + const char *updated_value = "UpdatedValue"; + + // Set the environment variable + if (setenv(env_var, initial_value, 1) != 0) { + perror("setenv failed"); + return EXIT_FAILURE; + } + + // Retrieve the environment variable + const char *value = getenv(env_var); + if (!value) { + fprintf(stderr, "getenv failed: Environment variable %s not found.\n", env_var); + return EXIT_FAILURE; + } + + // Verify the retrieved value matches the set value + if (strcmp(value, initial_value) != 0) { + fprintf(stderr, "Mismatch: Expected '%s', but got '%s'.\n", initial_value, value); + return EXIT_FAILURE; + } + + // Update the environment variable + if (setenv(env_var, updated_value, 1) != 0) { + perror("setenv failed (update)"); + return EXIT_FAILURE; + } + + // Retrieve the updated environment variable + value = getenv(env_var); + if (!value) { + fprintf(stderr, "getenv failed: Environment variable %s not found after update.\n", env_var); + return EXIT_FAILURE; + } + + // Verify the retrieved value matches the updated value + if (strcmp(value, updated_value) != 0) { + fprintf(stderr, "Mismatch after update: Expected '%s', but got '%s'.\n", updated_value, value); + return EXIT_FAILURE; + } + + // Print success message + printf("Environment variable %s tested successfully.\n", env_var); + + return EXIT_SUCCESS; +} diff --git a/programs/tests/t_exec.c b/programs/tests/t_exec.c index 676d38b1..c33f64fe 100644 --- a/programs/tests/t_exec.c +++ b/programs/tests/t_exec.c @@ -9,56 +9,44 @@ #include #include -static inline void __print_usage(int argc, char *argv[]) -{ - if (argc > 0) { - printf("%s: Usage: %s \n", argv[0], argv[0]); - printf("exec_type: execl, execlp, execle, execlpe, execv, execvp, execve, execvpe\n"); - } -} - int main(int argc, char *argv[]) { + pid_t pid; int status; - if (argc != 2) { - __print_usage(argc, argv); - return 1; - } - if (setenv("ENV_VAR", "pwd0", 0) == -1) { - printf("Failed to set env: `ENV_VAR`\n"); - return 1; + // Fork a new process + pid = fork(); + + if (pid < 0) { + // Error in forking + perror("fork"); + return EXIT_FAILURE; } - char *file = "echo"; - char *path = "/bin/echo"; - char *exec_argv[] = { "echo", "ENV_VAR: ${ENV_VAR}", NULL }; - char *exec_envp[] = { "PATH=/bin", "ENV_VAR=pwd1", NULL }; + if (pid == 0) { + // Child process: Use exec to replace the child process image + + // Program to execute: /bin/echo + // Arguments: /bin/echo "Exec test successful" + execl("/bin/echo", "echo", "Exec test successful", (char *)NULL); + + // If exec fails, print an error and exit + perror("execl"); + exit(EXIT_FAILURE); + } else { + // Parent process: Wait for the child process to complete + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return EXIT_FAILURE; + } - if (fork() == 0) { - if (strcmp(argv[1], "execl") == 0) { - execl(path, "echo", "ENV_VAR: ${ENV_VAR}", NULL); - } else if (strcmp(argv[1], "execlp") == 0) { - execlp(file, "echo", "ENV_VAR: ${ENV_VAR}", NULL); - } else if (strcmp(argv[1], "execle") == 0) { - execle(path, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); - } else if (strcmp(argv[1], "execlpe") == 0) { - execlpe(file, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); - } else if (strcmp(argv[1], "execv") == 0) { - execv(path, exec_argv); - } else if (strcmp(argv[1], "execvp") == 0) { - execvp(file, exec_argv); - } else if (strcmp(argv[1], "execve") == 0) { - execve(path, exec_argv, exec_envp); - } else if (strcmp(argv[1], "execvpe") == 0) { - execvpe(file, exec_argv, exec_envp); + // Check if the child terminated successfully + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + // exec worked + return EXIT_SUCCESS; } else { - __print_usage(argc, argv); - return 1; + // exec failed or child terminated abnormally + return EXIT_FAILURE; } - printf("Exec failed.\n"); - return 1; } - wait(&status); - return 0; } diff --git a/programs/tests/t_fork.c b/programs/tests/t_fork.c index b31b6514..a78dffa5 100644 --- a/programs/tests/t_fork.c +++ b/programs/tests/t_fork.c @@ -15,42 +15,42 @@ int main(int argc, char *argv[]) { - // Check if the correct number of arguments is provided. - if (argc != 2) { - fprintf(STDERR_FILENO, "Usage: %s \n", argv[0]); - return EXIT_FAILURE; - } + pid_t pid; + + // Fork a new process + pid = fork(); - // Convert the argument to an integer. - char *ptr; - int N = strtol(argv[1], &ptr, 10); - // Check if the conversion was successful and the number is non-negative. - if (*ptr != '\0' || N < 0) { - fprintf(STDERR_FILENO, "Invalid number: %s\n", argv[1]); + if (pid < 0) { + // Error in forking + perror("fork"); return EXIT_FAILURE; } - pid_t cpid, mypid; + if (pid == 0) { + // Child process + printf("Child process: PID = %d, Parent PID = %d\n", getpid(), getppid()); + // Simulate some work in the child + sleep(1); + printf("Child process exiting successfully.\n"); + exit(EXIT_SUCCESS); + } else { + // Parent process + printf("Parent process: PID = %d, Child PID = %d\n", getpid(), pid); - while (1) { - mypid = getpid(); // Get the current process ID. - if (N > 0) { - // Fork a new process. - if ((cpid = fork()) == 0) { - // In the child process, decrement N and continue the loop. - N -= 1; - continue; - } - // In the parent process, print a message and wait for the child to finish. - printf("I'm %d and I will wait for %d (N = %d)!\n", mypid, cpid, N); - while (wait(NULL) != -1) continue; - printf("I'm %d and I waited for %d (N = %d)!\n", mypid, cpid, N); + // Wait for the child process to complete + int status; + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return EXIT_FAILURE; + } + + // Check if the child exited normally + if (WIFEXITED(status)) { + printf("Parent process: Child exited with status %d.\n", WEXITSTATUS(status)); + return EXIT_SUCCESS; } else { - // If N is 0, print a message and exit the loop. - printf("I'm %d and I will not wait!\n", mypid); + printf("Parent process: Child did not exit normally.\n"); + return EXIT_FAILURE; } - break; } - - return EXIT_SUCCESS; } diff --git a/programs/tests/t_getenv.c b/programs/tests/t_getenv.c deleted file mode 100644 index 10f8d9b7..00000000 --- a/programs/tests/t_getenv.c +++ /dev/null @@ -1,26 +0,0 @@ -/// @file t_getenv.c -/// @brief Test the getenv function. -/// @details This program tests the `getenv` function by retrieving the value of -/// an environment variable and printing it. If the environment variable is not -/// set, it prints an error message and exits with a failure status. -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - // Retrieve the value of the environment variable "ENV_VAR". - char *env_var = getenv("ENV_VAR"); - if (env_var == NULL) { - fprintf(STDERR_FILENO, "Failed to get env: `ENV_VAR`: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - // Print the value of the environment variable. - printf("ENV_VAR = %s\n", env_var); - return EXIT_SUCCESS; -} diff --git a/programs/tests/t_groups.c b/programs/tests/t_groups.c index f2254842..34ccfb8e 100644 --- a/programs/tests/t_groups.c +++ b/programs/tests/t_groups.c @@ -33,8 +33,9 @@ int main(int argc, char **argv) pid_t ppid_child = getppid(); pid_t sid_child = getsid(ppid_child); - // Sleep for a number of seconds based on the loop index. - sleep(i + 1); + // Request to sleep for (i * 100 ms). + struct timespec req = { 0, i * 100000000 }; + nanosleep(&req, NULL); // Print the IDs of the child process and its parent. printf("%d) pid_child: %d, gid_child: %d, ppid_child: %d, sid_child: %d\n", diff --git a/programs/tests/t_hashmap.c b/programs/tests/t_hashmap.c new file mode 100644 index 00000000..fe4e7c5b --- /dev/null +++ b/programs/tests/t_hashmap.c @@ -0,0 +1,98 @@ +/// @file t_hashmap.c +/// @brief This program tests hashmaps. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +#include "hashmap.h" + +// Custom allocation function for hashmap entries +hashmap_entry_t *alloc_entry(void) +{ + hashmap_entry_t *entry = (hashmap_entry_t *)malloc(sizeof(hashmap_entry_t)); + assert(entry && "Failed to allocate hashmap entry."); + return entry; +} + +// Custom deallocation function for hashmap entries +void dealloc_entry(hashmap_entry_t *entry) +{ + assert(entry && "Invalid entry pointer."); + free(entry); // Free the entry itself +} + +int main(void) +{ + hashmap_t map; + hashmap_init(&map, alloc_entry, dealloc_entry); + + // Test inserting and retrieving values + hashmap_insert(&map, "apple", "A sweet red fruit"); + hashmap_insert(&map, "banana", "A long yellow fruit"); + hashmap_insert(&map, "grape", "A small purple or green fruit"); + + if (strcmp(hashmap_get(&map, "apple"), "A sweet red fruit") != 0) { + fprintf(stderr, "Error: Failed to retrieve 'apple'\n"); + return 1; + } + if (strcmp(hashmap_get(&map, "banana"), "A long yellow fruit") != 0) { + fprintf(stderr, "Error: Failed to retrieve 'banana'\n"); + return 1; + } + if (strcmp(hashmap_get(&map, "grape"), "A small purple or green fruit") != 0) { + fprintf(stderr, "Error: Failed to retrieve 'grape'\n"); + return 1; + } + + // Test retrieving a non-existent key + if (hashmap_get(&map, "orange") != NULL) { + fprintf(stderr, "Error: Retrieved value for non-existent key 'orange'\n"); + return 1; + } + + // Test updating an existing key + hashmap_insert(&map, "apple", "A popular fruit often red or green"); + if (strcmp(hashmap_get(&map, "apple"), "A popular fruit often red or green") != 0) { + fprintf(stderr, "Error: Failed to update value for 'apple'\n"); + return 1; + } + + // Test removing a key + hashmap_remove(&map, "banana"); + if (hashmap_get(&map, "banana") != NULL) { + fprintf(stderr, "Error: Key 'banana' was not removed\n"); + return 1; + } + + // Test removing a non-existent key (should not cause any errors) + hashmap_remove(&map, "pineapple"); + + // Test reinserting and retrieval after some removals + hashmap_insert(&map, "banana", "A reinserted long yellow fruit"); + if (strcmp(hashmap_get(&map, "banana"), "A reinserted long yellow fruit") != 0) { + fprintf(stderr, "Error: Failed to retrieve reinserted 'banana'\n"); + return 1; + } + + // Test the removal of all items and final cleanup + hashmap_destroy(&map); + if (hashmap_get(&map, "apple") != NULL) { + fprintf(stderr, "Error: Key 'apple' still exists after destroy\n"); + return 1; + } + if (hashmap_get(&map, "grape") != NULL) { + fprintf(stderr, "Error: Key 'grape' still exists after destroy\n"); + return 1; + } + if (hashmap_get(&map, "banana") != NULL) { + fprintf(stderr, "Error: Key 'banana' still exists after destroy\n"); + return 1; + } + + return 0; +} diff --git a/programs/tests/t_itimer.c b/programs/tests/t_itimer.c index 487726b6..ae6f5e74 100644 --- a/programs/tests/t_itimer.c +++ b/programs/tests/t_itimer.c @@ -16,70 +16,66 @@ #include #include -/// @brief Signal handler for SIGALRM. -/// @param sig The signal number. -void alarm_handler(int sig) -{ - printf("handler(%d) : Starting handler.\n", sig); - if (sig == SIGALRM) { - struct itimerval val = { 0 }; - // Get the current value of the interval timer. - if (getitimer(ITIMER_REAL, &val) == -1) { - perror("getitimer failed"); - exit(EXIT_FAILURE); - } - printf("(sec: %ld, usec: %ld)\n", val.it_interval.tv_sec, val.it_interval.tv_usec); +// Global variables to track timer events +volatile int timer_fired = 0; +int timer_count = 0; - static int counter = 0; - counter += 1; - - printf("handler(%d) : Correct signal x%d\n", sig, counter); - if (counter == 4) { - struct itimerval interval = { 0 }, prev = { 0 }; - // Stop the interval timer. - if (setitimer(ITIMER_REAL, &interval, &prev) == -1) { - perror("setitimer failed"); - exit(EXIT_FAILURE); - } - printf("prev: (sec: %ld, usec: %ld)\n", prev.it_interval.tv_sec, prev.it_interval.tv_usec); - exit(EXIT_SUCCESS); - } - } else { - printf("handler(%d) : Wrong signal.\n", sig); - } - printf("handler(%d) : Ending handler.\n", sig); +// Signal handler for the timer +void timer_handler(int signum) +{ + timer_fired = 1; + timer_count++; } -int main(int argc, char *argv[]) +int main(void) { - struct sigaction action; struct itimerval timer; + time_t start_time, current_time; - memset(&action, 0, sizeof(action)); - memset(&timer, 0, sizeof(timer)); + // Set up the signal handler for SIGALRM + signal(SIGALRM, timer_handler); - action.sa_handler = alarm_handler; + // Configure the timer to expire after 250 ms + timer.it_value.tv_sec = 0; // Initial expiration (seconds) + timer.it_value.tv_usec = 250000; // Initial expiration (microseconds) - // Set up the signal handler for SIGALRM. - if (sigaction(SIGALRM, &action, NULL) == -1) { - fprintf(STDERR_FILENO, "Failed to set signal handler: %s\n", strerror(errno)); + // Configure periodicity: 250 ms interval + timer.it_interval.tv_sec = 0; // Periodic interval (seconds) + timer.it_interval.tv_usec = 250000; // Periodic interval (microseconds) + + // Start the timer + if (setitimer(ITIMER_REAL, &timer, NULL) == -1) { + perror("setitimer"); return EXIT_FAILURE; } - // Configure the timer to expire after 1 second and then every 1 second. - timer.it_value.tv_sec = 1; // Initial delay. + // Record the start time + start_time = time(NULL); + + // Test periodicity: wait for 5 timer events + while (timer_count < 5) { + if (timer_fired) { + timer_fired = 0; // Reset the flag + + // Record the current time + current_time = time(NULL); + + // Print the elapsed time since the start + printf("Timer event %d fired at %ld seconds since start\n", timer_count, current_time - start_time); + } + } + + // Stop the timer + timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 0; - timer.it_interval.tv_sec = 1; // Interval for periodic timer. + timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; - - // Start the interval timer. if (setitimer(ITIMER_REAL, &timer, NULL) == -1) { - perror("setitimer failed"); + perror("setitimer (stop)"); return EXIT_FAILURE; } - // Infinite loop to keep the program running until the timer stops. - while (1) {} + printf("Test completed: Timer fired %d times\n", timer_count); return EXIT_SUCCESS; } diff --git a/programs/tests/t_kill.c b/programs/tests/t_kill.c index d7312ee3..3fe11ff7 100644 --- a/programs/tests/t_kill.c +++ b/programs/tests/t_kill.c @@ -48,16 +48,25 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; // Return failure if handler setup fails } + // Request to sleep for 100 ms. + struct timespec req = { 0, 100000000 }; + // Child process loop - waiting for signals while (1) { printf("I'm the child (pid: %d): I'm waiting...\n", cpid); - sleep(1); // Sleep for 1 second in each loop iteration + // Sleep for 100 ms. + nanosleep(&req, NULL); } } else if (cpid > 0) { // Parent process printf("I'm the parent (pid: %d)!\n", getpid()); - sleep(2); // Wait before sending the signal to the child + + // Request to sleep for 500 ms. + struct timespec req = { 0, 500000000 }; + + // Sleep for 500 ms. + nanosleep(&req, NULL); // Send SIGUSR1 to the child process if (kill(cpid, SIGUSR1) == -1) { @@ -65,7 +74,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; // Return failure if signal sending fails } - sleep(2); // Wait before terminating the child process + // Wait before terminating the child process, sleep for 500 ms. + nanosleep(&req, NULL); // Send SIGTERM to the child process to terminate it if (kill(cpid, SIGTERM) == -1) { diff --git a/programs/tests/t_list.c b/programs/tests/t_list.c new file mode 100644 index 00000000..883f4996 --- /dev/null +++ b/programs/tests/t_list.c @@ -0,0 +1,123 @@ +/// @file t_list.c +/// @brief This program tests lists. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +#include "list.h" // Include the list.h header file here + +// Custom allocation and deallocation functions for list nodes +static inline listnode_t *node_alloc(void) +{ + listnode_t *node = (listnode_t *)malloc(sizeof(listnode_t)); + assert(node && "Failed to allocate node."); + memset(node, 0, sizeof(listnode_t)); + return node; +} + +void node_dealloc(listnode_t *node) +{ + assert(node && "Invalid node pointer."); + free(node); +} + +int main(void) +{ + // Initialize lists with custom alloc/dealloc functions + list_t list1, list2; + list_init(&list1, node_alloc, node_dealloc); + list_init(&list2, node_alloc, node_dealloc); + + // Test insertion at the front and back + list_insert_front(&list1, "apple"); + list_insert_back(&list1, "banana"); + list_insert_front(&list1, "cherry"); + if (list_size(&list1) != 3) { + printf("Error: list_insert_front or list_insert_back failed\n"); + return 1; + } + + // Test peeking at the front and back + if (strcmp(list_peek_front(&list1), "cherry") != 0) { + printf("Error: list_peek_front failed\n"); + return 1; + } + if (strcmp(list_peek_back(&list1), "banana") != 0) { + printf("Error: list_peek_back failed\n"); + return 1; + } + + // Test list size and empty check + if (list_size(&list1) != 3) { + printf("Error: list_size failed\n"); + return 1; + } + if (list_empty(&list1)) { + printf("Error: list_empty failed\n"); + return 1; + } + + // Test remove from front + char *removed = list_remove_front(&list1); + if (strcmp(removed, "cherry") != 0) { + printf("Error: list_remove_front failed\n"); + return 1; + } + if (list_size(&list1) != 2) { + printf("Error: list size after list_remove_front is incorrect\n"); + return 1; + } + + // Test remove from back + removed = list_remove_back(&list1); + if (strcmp(removed, "banana") != 0) { + printf("Error: list_remove_back failed\n"); + return 1; + } + if (list_size(&list1) != 1) { + printf("Error: list size after list_remove_back is incorrect\n"); + return 1; + } + + // Test find + list_insert_back(&list1, "banana"); + listnode_t *found_node = list_find(&list1, "banana"); + if (!(found_node && strcmp(found_node->value, "banana") == 0)) { + printf("Error: list_find failed\n"); + return 1; + } + + // Test merging lists + list_insert_back(&list1, "fig"); + list_insert_back(&list1, "grape"); + list_insert_back(&list2, "honeydew"); + list_insert_back(&list2, "kiwi"); + list_merge(&list1, &list2); + if (!list_empty(&list2)) { + printf("Error: list_merge failed; source list is not empty\n"); + return 1; + } + if (list_size(&list1) != 6) { + printf("Error: list size after list_merge is incorrect\n"); + return 1; + } + + // Test destruction + list_destroy(&list1); + list_destroy(&list2); + if (!list_empty(&list1) || list_size(&list1) != 0) { + printf("Error: list_destroy failed for list1\n"); + return 1; + } + if (!list_empty(&list2) || list_size(&list2) != 0) { + printf("Error: list_destroy failed for list2\n"); + return 1; + } + + // If all tests passed, return 0 + return 0; +} diff --git a/programs/tests/t_mem.c b/programs/tests/t_mem.c index c5a66b2a..c471ca6e 100644 --- a/programs/tests/t_mem.c +++ b/programs/tests/t_mem.c @@ -10,48 +10,28 @@ #include #include #include -#include #include int main(int argc, char *argv[]) { - // Ensure the program is provided with exactly 2 arguments (row and column size) - if (argc != 3) { - fprintf(STDERR_FILENO, "%s: You must provide 2 dimensions (rows and columns).\n", argv[0]); - return EXIT_FAILURE; - } - - char *ptr; - // Convert the first argument (rows) to an integer and validate it - int rows = strtol(argv[1], &ptr, 10); - if (*ptr != '\0' || rows <= 0) { - fprintf(STDERR_FILENO, "Invalid number of rows: %s\n", argv[1]); - return EXIT_FAILURE; - } - - // Convert the second argument (columns) to an integer and validate it - int cols = strtol(argv[2], &ptr, 10); - if (*ptr != '\0' || cols <= 0) { - fprintf(STDERR_FILENO, "Invalid number of columns: %s\n", argv[2]); - return EXIT_FAILURE; - } - - printf("Allocating memory!\n"); - pr_warning("Allocating memory!\n"); + // Define dimensions for the 2D array (can be modified for testing). + const int rows = 100; // Number of rows. + const int cols = 100; // Number of columns. - // Allocate memory for an array of row pointers (rows x cols matrix) + // Allocate memory for an array of row pointers (rows x cols matrix). int **M = (int **)malloc(rows * sizeof(int *)); if (M == NULL) { - perror("Failed to allocate memory for row pointers"); + fprintf(stderr, "Failed to allocate memory for row pointers.\n"); return EXIT_FAILURE; } - // Allocate memory for each row (array of integers) and check for allocation errors + // Allocate memory for each row (array of integers) and check for allocation errors. for (int i = 0; i < rows; ++i) { M[i] = (int *)malloc(cols * sizeof(int)); if (M[i] == NULL) { - perror("Failed to allocate memory for columns"); - // Free any previously allocated memory to prevent memory leaks + fprintf(stderr, "Failed to allocate memory for row %d.\n", i); + + // Free any previously allocated memory to prevent memory leaks. for (int j = 0; j < i; ++j) { free(M[j]); } @@ -60,46 +40,32 @@ int main(int argc, char *argv[]) } } - // Simulate delay for demonstration purposes - sleep(5); - printf("Writing memory!\n"); - pr_warning("Writing memory!\n"); - - // Write values to the 2D array (fill with i + j) + // Write values to the 2D array (fill with i + j). for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { M[i][j] = i + j; } } - // Simulate delay for demonstration purposes - sleep(5); - printf("Freeing memory (1)!\n"); - pr_warning("Freeing memory (1)!\n"); - - // Free the memory for odd-indexed rows + // Free the memory for odd-indexed rows. for (int i = 0; i < rows; ++i) { if (i % 2 != 0) { free(M[i]); + M[i] = NULL; // Nullify pointer after freeing. } } - printf("Freeing memory (2)!\n"); - pr_warning("Freeing memory (2)!\n"); - - // Free the memory for even-indexed rows + // Free the memory for even-indexed rows. for (int i = 0; i < rows; ++i) { if (i % 2 == 0) { free(M[i]); + M[i] = NULL; // Nullify pointer after freeing. } } - // Free the memory allocated for the row pointers array + // Free the memory allocated for the row pointers array. free(M); - - // Simulate delay before exiting - sleep(5); - pr_warning("Exiting!\n"); + M = NULL; // Nullify pointer after freeing. return EXIT_SUCCESS; } diff --git a/programs/tests/t_mkdir.c b/programs/tests/t_mkdir.c index 14c45851..ba74ea9f 100644 --- a/programs/tests/t_mkdir.c +++ b/programs/tests/t_mkdir.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/programs/tests/t_msgget.c b/programs/tests/t_msgget.c index b552aa75..36ac59e4 100644 --- a/programs/tests/t_msgget.c +++ b/programs/tests/t_msgget.c @@ -10,7 +10,7 @@ /// See LICENSE.md for details. #include -#include +#include #include #include #include @@ -69,6 +69,9 @@ int main(int argc, char *argv[]) key_t key; int msqid; + // Request to sleep for 200 ms. + struct timespec req = { 0, 200000000 }; + // ======================================================================== // Generating a key using ftok key = ftok("/README.md", 5); @@ -94,14 +97,16 @@ int main(int argc, char *argv[]) __receive_message(msqid, 1, &message); // Create child process. if (!fork()) { - sleep(3); + // Sleep for 200 ms. + nanosleep(&req, NULL); // Send the message. __send_message(msqid, 1, &message, "General Kenobi..."); return EXIT_SUCCESS; } // Receive the message. __receive_message(msqid, 1, &message); - sleep(3); + // Sleep for 200 ms. + nanosleep(&req, NULL); // ======================================================================== // Send multiple messages. diff --git a/programs/tests/t_ndtree.c b/programs/tests/t_ndtree.c new file mode 100644 index 00000000..d2c1268d --- /dev/null +++ b/programs/tests/t_ndtree.c @@ -0,0 +1,120 @@ +/// @file t_ndtree.c +/// @brief This program tests N-Dimensional trees. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ndtree.h" + +#include +#include +#include + +// Custom allocator for tree nodes +ndtree_node_t *custom_alloc_node(void *value) +{ + ndtree_node_t *node = (ndtree_node_t *)malloc(sizeof(ndtree_node_t)); + if (node) { + node->value = value; + } + return node; +} + +// Custom deallocator for tree nodes +void custom_free_node(ndtree_node_t *node) +{ + free(node); +} + +// Comparison function for integer values +int compare_node(void *lhs, void *rhs) +{ + return (*(int *)lhs) - (*(int *)rhs); +} + +// Function to print node values (assuming integer values for simplicity) +void print_node(ndtree_node_t *node) +{ + printf("Node value: %d\n", *(int *)(node->value)); +} + +int main(void) +{ + // Initialize the tree + ndtree_t tree; + ndtree_tree_init(&tree, compare_node, custom_alloc_node, custom_free_node); + + // Create the root node + int root_value = 1; + ndtree_node_t *root = ndtree_create_root(&tree, &root_value); + if (!root) { + fprintf(stderr, "Error: Failed to create root node\n"); + return 1; + } + + // Add child nodes to the root (level 2) + int child1_value = 2, child2_value = 3, child3_value = 4; + ndtree_node_t *child1 = ndtree_create_child_of_node(&tree, root, &child1_value); + ndtree_node_t *child2 = ndtree_create_child_of_node(&tree, root, &child2_value); + ndtree_node_t *child3 = ndtree_create_child_of_node(&tree, root, &child3_value); + if (!child1 || !child2 || !child3) { + fprintf(stderr, "Error: Failed to create one or more child nodes for root\n"); + return 1; + } + + // Add child nodes to child1 (level 3) + int child1_1_value = 5, child1_2_value = 6; + if (!ndtree_create_child_of_node(&tree, child1, &child1_1_value) || + !ndtree_create_child_of_node(&tree, child1, &child1_2_value)) { + fprintf(stderr, "Error: Failed to create one or more child nodes for child1\n"); + return 1; + } + + // Add child nodes to child2 (level 3) + int child2_1_value = 7, child2_2_value = 8; + if (!ndtree_create_child_of_node(&tree, child2, &child2_1_value) || + !ndtree_create_child_of_node(&tree, child2, &child2_2_value)) { + fprintf(stderr, "Error: Failed to create one or more child nodes for child2\n"); + return 1; + } + + // Add child nodes to child3 (level 3) + int child3_1_value = 9, child3_2_value = 10; + if (!ndtree_create_child_of_node(&tree, child3, &child3_1_value) || + !ndtree_create_child_of_node(&tree, child3, &child3_2_value)) { + fprintf(stderr, "Error: Failed to create one or more child nodes for child3\n"); + return 1; + } + + // Verify the number of children at each level + if (ndtree_node_count_children(root) != 3) { + fprintf(stderr, "Error: Expected root to have 3 children\n"); + return 1; + } + if (ndtree_node_count_children(child1) != 2) { + fprintf(stderr, "Error: Expected child1 to have 2 children\n"); + return 1; + } + if (ndtree_node_count_children(child2) != 2) { + fprintf(stderr, "Error: Expected child2 to have 2 children\n"); + return 1; + } + if (ndtree_node_count_children(child3) != 2) { + fprintf(stderr, "Error: Expected child3 to have 2 children\n"); + return 1; + } + + // Traverse the tree and print values using the visitor function + ndtree_tree_visitor(&tree, print_node, NULL); + + // Test removing a node with children and verify + ndtree_tree_remove_node(&tree, child2, NULL); + if (ndtree_node_count_children(root) != 2) { + fprintf(stderr, "Error: Expected root to have 2 children after removing child2\n"); + return 1; + } + + // Final deallocation of the tree + ndtree_tree_dealloc(&tree, NULL); + + return 0; +} diff --git a/programs/tests/t_pipe_blocking.c b/programs/tests/t_pipe_blocking.c new file mode 100644 index 00000000..f42c22c0 --- /dev/null +++ b/programs/tests/t_pipe_blocking.c @@ -0,0 +1,79 @@ +/// @file t_pipe_blocking.c +/// @brief Test blocking pipe operations between parent and child process. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include + +int main(void) +{ + int fds[2]; + char write_msg[] = "Blocking test message"; + char read_msg[sizeof(write_msg)] = { 0 }; + ssize_t bytes_written, bytes_read; + int error_code = 0; + + // Create a pipe. + if (pipe(fds) == -1) { + fprintf(stderr, "Failed to create pipe\n"); + return 1; + } + + // Fork a child process. + pid_t pid = fork(); + + if (pid == -1) { + fprintf(stderr, "Failed to fork process\n"); + close(fds[0]); + close(fds[1]); + return 1; + } else if (pid == 0) { + // Child process: reads from the pipe. + close(fds[1]); // Close unused write end + + printf("Child waiting to read from pipe...\n"); + do { + bytes_read = read(fds[0], read_msg, sizeof(read_msg)); + if (bytes_read > 0) { + printf("Child read message: '%s' (%ld bytes)\n", read_msg, bytes_read); + } else if ((bytes_read == -1) && (errno != EAGAIN)) { + fprintf(stderr, "Error occurred during read in child process\n"); + error_code = 1; + break; + } + } while (bytes_read != 0); + + close(fds[0]); // Close read end + return error_code; + + } else { + // Parent process: writes to the pipe. + close(fds[0]); // Close unused read end. + + // Sleep for 200 ms. + timespec_t req = { 0, 200000000 }; + nanosleep(&req, NULL); + + printf("Parent writing to pipe...\n"); + bytes_written = write(fds[1], write_msg, sizeof(write_msg)); + + if (bytes_written > 0) { + printf("Parent wrote message: '%s' (%ld bytes)\n", write_msg, bytes_written); + } else if (bytes_written == -1) { + fprintf(stderr, "Error occurred during write in parent process\n"); + error_code = 1; + } + + // Sleep for 200 ms. + nanosleep(&req, NULL); + + close(fds[1]); // Close write end. + wait(NULL); // Wait for child to finish + return error_code; + } +} diff --git a/programs/tests/t_pipe_non_blocking.c b/programs/tests/t_pipe_non_blocking.c new file mode 100644 index 00000000..6546a8b4 --- /dev/null +++ b/programs/tests/t_pipe_non_blocking.c @@ -0,0 +1,98 @@ +/// @file t_pipe_non_blocking.c +/// @brief Test non-blocking pipe operations within a single process. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + int fds[2]; + char write_msg[] = "Blocking test message"; + char read_msg[sizeof(write_msg)] = { 0 }; + ssize_t bytes_written, bytes_read; + int error_code = 0; + + // Create a pipe. + if (pipe(fds) == -1) { + fprintf(stderr, "Failed to create pipe\n"); + return 1; + } + + // Set both ends of the pipe to non-blocking mode. + if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1) { + fprintf(stderr, "Failed to set pipe to non-blocking mode\n"); + close(fds[0]); + close(fds[1]); + return 1; + } + + // Fork a child process. + pid_t pid = fork(); + + if (pid == -1) { + fprintf(stderr, "Failed to fork process\n"); + close(fds[0]); + close(fds[1]); + return 1; + } else if (pid == 0) { + // Child process: reads from the pipe. + close(fds[1]); // Close unused write end + + // Request to sleep for 100 ms. + struct timespec req = { 0, 100000000 }; + + printf("Child waiting to read from pipe...\n"); + do { + bytes_read = read(fds[0], read_msg, sizeof(read_msg)); + if (bytes_read > 0) { + printf("Child read message: '%s' (%ld bytes)\n", read_msg, bytes_read); + } else if (bytes_read == -1) { + if (errno != EAGAIN) { + fprintf(stderr, "Error occurred during read in child process\n"); + error_code = 1; + break; + } else { + printf("Child has nothing to read...\n"); + nanosleep(&req, NULL); + } + } + } while (bytes_read != 0); + + close(fds[0]); // Close read end + return error_code; + + } else { + // Parent process: writes to the pipe. + close(fds[0]); // Close unused read end. + + // Request to sleep for 500 ms. + struct timespec req = { 0, 500000000 }; + + // Sleep for 500 ms. + nanosleep(&req, NULL); + + printf("Parent writing to pipe...\n"); + bytes_written = write(fds[1], write_msg, sizeof(write_msg)); + + if (bytes_written > 0) { + printf("Parent wrote message: '%s' (%ld bytes)\n", write_msg, bytes_written); + } else if (bytes_written == -1) { + fprintf(stderr, "Error occurred during write in parent process\n"); + error_code = 1; + } + + // Sleep for 500 ms. + nanosleep(&req, NULL); + + close(fds[1]); // Close write end. + wait(NULL); // Wait for child to finish + return error_code; + } +} diff --git a/programs/tests/t_semflg.c b/programs/tests/t_semflg.c index 58773329..2b713156 100644 --- a/programs/tests/t_semflg.c +++ b/programs/tests/t_semflg.c @@ -9,7 +9,7 @@ /// This file is distributed under the MIT License. See LICENSE.md for details. #include -#include +#include #include #include #include @@ -62,7 +62,9 @@ int main(int argc, char *argv[]) op_child.sem_op = 1; // Increment by 1. op_child.sem_flg = 0; // No special flags. - sleep(3); // Simulate delay before child performs the operation. + // Sleep for 200 ms. + timespec_t req = { 0, 200000000 }; + nanosleep(&req, NULL); // Perform the increment operation on the semaphore. if (semop(semid, &op_child, 1) < 0) { diff --git a/programs/tests/t_semget.c b/programs/tests/t_semget.c index a65ba2bb..4391e207 100644 --- a/programs/tests/t_semget.c +++ b/programs/tests/t_semget.c @@ -7,7 +7,7 @@ /// This file is distributed under the MIT License. See LICENSE.md for details. #include -#include +#include #include #include #include @@ -70,7 +70,9 @@ int main(int argc, char *argv[]) op_child.sem_op = 1; // Increment semaphore by 1. op_child.sem_flg = 0; // No special flags. - sleep(3); // Simulate some work before modifying the semaphore. + // Simulate some work before modifying the semaphore. + timespec_t req = { 0, 200000000 }; + nanosleep(&req, NULL); // Increment the semaphore, unblocking the parent. if (semop(semid, &op_child, 1) < 0) { @@ -88,7 +90,7 @@ int main(int argc, char *argv[]) printf("[child] Semaphore value is %d (expected: 2)\n", ret); // Sleep and perform another increment operation. - sleep(3); + nanosleep(&req, NULL); if (semop(semid, &op_child, 1) < 0) { perror("Failed to perform second child semaphore operation"); return 1; diff --git a/programs/tests/t_semop.c b/programs/tests/t_semop.c index 5c508c4c..514992a3 100644 --- a/programs/tests/t_semop.c +++ b/programs/tests/t_semop.c @@ -5,7 +5,7 @@ /// This file is distributed under the MIT License. See LICENSE.md for details. #include -#include +#include #include #include #include diff --git a/programs/tests/t_setenv.c b/programs/tests/t_setenv.c deleted file mode 100644 index 80b67d07..00000000 --- a/programs/tests/t_setenv.c +++ /dev/null @@ -1,63 +0,0 @@ -/// @file t_setenv.c -/// @brief Demonstrates the use of `setenv` to set an environment variable and -/// fork a child process that inherits the environment variable. The child -/// executes a different program (`t_getenv`) to verify the environment variable -/// is passed. -/// @copyright (c) 2014-2024 -/// This file is distributed under the MIT License. See LICENSE.md for details. - -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - // Arguments to pass to the child process (t_getenv) - char *_argv[] = { "/bin/tests/t_getenv", NULL }; - int status; - - // ======================================================================== - // Set environment variable "ENV_VAR" to "pwd0" without overwriting if it exists. - if (setenv("ENV_VAR", "pwd0", 0) == -1) { - perror("Failed to set environment variable `ENV_VAR`"); - return 1; - } - printf("Environment variable `ENV_VAR` set to `pwd0`\n"); - - // ======================================================================== - // Fork a child process to execute t_getenv. - pid_t pid = fork(); - if (pid < 0) { - perror("Failed to fork"); - return 1; - } - - // Child process - if (pid == 0) { - // Execute t_getenv to check the environment variable in the child. - execv(_argv[0], _argv); - - // If execv returns, something went wrong. - perror("Exec failed"); - return 1; // Terminate the child process with error if execv fails. - } - - // ======================================================================== - // Parent process waits for the child to complete. - if (wait(&status) == -1) { - perror("Failed to wait for child process"); - return 1; - } - - // Check the status of the child process - if (WIFEXITED(status)) { - printf("Child process exited with status: %d\n", WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - printf("Child process was terminated by signal: %d\n", WTERMSIG(status)); - } else { - printf("Child process did not exit normally.\n"); - } - - return 0; -} diff --git a/programs/tests/t_shm.c b/programs/tests/t_shm.c new file mode 100644 index 00000000..8b5a266f --- /dev/null +++ b/programs/tests/t_shm.c @@ -0,0 +1,131 @@ +/// @file t_shm.c +/// @brief A program that writes data to a shared memory segment using a key +/// generated from a file and an id. +/// @copyright (c) 2014-2024 +/// This file is distributed under the MIT License. See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +int shm_write(void) +{ + key_t key; + int shmid; + char *str; + + const char *message = "Hello there!"; + const char *path = "/home"; + const int id = 42; + + // Generate a System V IPC key using the predefined file path and id. + key = ftok(path, id); + if (key == -1) { + syslog(LOG_ERR, "Failed to generate IPC key using ftok."); + return EXIT_FAILURE; + } + + // Create a shared memory segment with the generated key, size of 1024 bytes, + // and permissions 0666. + shmid = shmget(key, 1024, IPC_CREAT | 0666); + if (shmid == -1) { + syslog(LOG_ERR, "Failed to create shared memory segment using shmget."); + return EXIT_FAILURE; + } + + // Attach the shared memory segment to the process's address space. + str = (char *)shmat(shmid, NULL, 0); + if (str == (char *)-1) { + syslog(LOG_ERR, "Failed to attach shared memory segment using shmat."); + return EXIT_FAILURE; + } + + // Write a message to the shared memory, ensuring no buffer overflow. + strncpy(str, message, strlen(message)); + str[1023] = '\0'; // Ensure null termination. + + // Detach the shared memory segment from the process's address space. + if (shmdt(str) < 0) { + syslog(LOG_ERR, "Failed to detach shared memory segment using shmdt."); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int shm_read(void) +{ + key_t key; + int shmid; + char *str; + + const char *message = "Hello there!"; + const char *path = "/home"; + const int id = 42; + + // Generate the IPC key using ftok with the predefined file and id. + key = ftok(path, id); + if (key == -1) { + syslog(LOG_ERR, "Failed to generate IPC key using ftok."); + return EXIT_FAILURE; + } + + // Create or locate a shared memory segment based on the key. + // Size is set to 1024 bytes, and 0666 allows read/write permissions. + shmid = shmget(key, 1024, 0666); + if (shmid == -1) { + syslog(LOG_ERR, "Failed to create or access shared memory using shmget."); + return EXIT_FAILURE; + } + + // Attach the process to the shared memory segment in read-only mode (SHM_RDONLY). + str = (char *)shmat(shmid, NULL, SHM_RDONLY); + if (str == (char *)-1) { + syslog(LOG_ERR, "Failed to attach to shared memory segment using shmat."); + return EXIT_FAILURE; + } + + // Check if both hashes match. + if (strncmp(str, message, strlen(message))) { + syslog(LOG_ERR, "Data does not match."); + syslog(LOG_ERR, "Expected : `%s`", message); + syslog(LOG_ERR, "Found : `%s`", str); + return EXIT_FAILURE; + } + + // Detach the process from the shared memory segment after use. + if (shmdt(str) < 0) { + syslog(LOG_ERR, "Failed to detach shared memory segment using shmdt."); + return EXIT_FAILURE; + } + + // Mark the shared memory segment for removal (IPC_RMID). + if (shmctl(shmid, IPC_RMID, NULL) == -1) { + syslog(LOG_ERR, "Failed to mark shared memory segment for removal using shmctl."); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + // Open a connection to the syslog. + openlog("t_shm", LOG_PID | LOG_CONS, LOG_USER); + if (shm_write()) { + syslog(LOG_ERR, "Function shm_write failed."); + closelog(); + return EXIT_FAILURE; + } + if (shm_read()) { + syslog(LOG_ERR, "Function shm_read failed."); + closelog(); + return EXIT_FAILURE; + } + closelog(); + return EXIT_SUCCESS; +} diff --git a/programs/tests/t_shm_read.c b/programs/tests/t_shm_read.c deleted file mode 100644 index 2481d54c..00000000 --- a/programs/tests/t_shm_read.c +++ /dev/null @@ -1,81 +0,0 @@ -/// @file t_shm_read.c -/// @brief A program that reads data from a shared memory segment using a key -/// generated from a file and an id. -/// @copyright (c) 2014-2024 -/// This file is distributed under the MIT License. See LICENSE.md for details. - -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - key_t key; - int shmid, id; - char *str, *ptr; - ; - char *path; - - // Ensure the correct number of command-line arguments are provided. - if (argc != 3) { - fprintf(stderr, "%s: You must provide a file and the id to generate the key.\n", argv[0]); - return EXIT_FAILURE; - } - - // Get the file path and the id from the command-line arguments. - path = argv[1]; - id = strtol(argv[2], &ptr, 10); - // Check if the conversion was successful and the number is non-negative. - if (*ptr != '\0' || id < 0) { - fprintf(STDERR_FILENO, "Invalid number: %s\n", argv[1]); - return EXIT_FAILURE; - } - - // Generate the IPC key using ftok with the provided file and id. - key = ftok(path, id); - if (key == -1) { - perror("ftok"); - return EXIT_FAILURE; - } - printf("id = %d; key = %d\n", id, key); - - // Create or locate a shared memory segment based on the key. The size is - // set to 1024 bytes, and 0666 allows read/write permissions. - shmid = shmget(key, 1024, 0666); - if (shmid == -1) { - perror("shmget"); - return EXIT_FAILURE; - } - printf("shmid = %d;\n", shmid); - - // Attach the process to the shared memory segment in read-only mode (SHM_RDONLY). - str = (char *)shmat(shmid, NULL, SHM_RDONLY); - if (str == (char *)-1) { - perror("shmat"); - return EXIT_FAILURE; - } - - printf("Data read from memory: %s (%p)\n", str, str); - - // Attempting to modify shared memory here would fail due to SHM_RDONLY mode. - // The following line is commented out as it will cause an error: - // str[0] = 'H'; // Would trigger a memory access violation - - // Detach the process from the shared memory segment after use. - if (shmdt(str) < 0) { - perror("shmdt"); - return EXIT_FAILURE; - } - - // Mark the shared memory segment for removal (IPC_RMID). - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - return EXIT_FAILURE; - } - - printf("Exiting.\n"); - return EXIT_SUCCESS; // Return success -} diff --git a/programs/tests/t_shm_write.c b/programs/tests/t_shm_write.c deleted file mode 100644 index d48c6fbb..00000000 --- a/programs/tests/t_shm_write.c +++ /dev/null @@ -1,74 +0,0 @@ -/// @file t_shm_write.c -/// @brief A program that writes data to a shared memory segment using a key -/// generated from a file and an id. -/// @copyright (c) 2014-2024 -/// This file is distributed under the MIT License. See LICENSE.md for details. - -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - key_t key; - int shmid, id; - char *str, *ptr; - char *path; - - // Ensure the correct number of command-line arguments are provided. - if (argc != 3) { - fprintf(stderr, "%s: You must provide a file and the id to generate the key.\n", argv[0]); - return EXIT_FAILURE; - } - - // Get the file path and the id from command-line arguments. - path = argv[1]; - id = strtol(argv[2], &ptr, 10); - - // Validate that the provided id is a valid integer. - if (*ptr != '\0') { - fprintf(stderr, "%s: Invalid id: '%s'. Please provide a valid integer.\n", argv[0], argv[2]); - return EXIT_FAILURE; - } - - // Generate a System V IPC key using the provided file path and id. - key = ftok(path, id); - if (key == -1) { - perror("ftok"); - return EXIT_FAILURE; - } - - // Create a shared memory segment with the generated key, size of 1024 - // bytes, and permissions 0666. - shmid = shmget(key, 1024, IPC_CREAT | 0666); - if (shmid == -1) { - perror("shmget"); - return EXIT_FAILURE; - } - - // Attach the shared memory segment to the process's address space. - str = (char *)shmat(shmid, NULL, 0); - // Ensure the shared memory was attached correctly. - if (str == (char *)-1) { - perror("shmat"); - return EXIT_FAILURE; - } - - // Write a message to the shared memory, ensuring no buffer overflow using strncpy. - const char *message = "Hello there!\n"; - // Copy the message to shared memory. - strncpy(str, message, 1024); - // Ensure the string is null-terminated. - str[1023] = '\0'; - - // Detach the shared memory segment from the process's address space. - if (shmdt(str) < 0) { - perror("shmdt"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/programs/tests/t_sigusr.c b/programs/tests/t_sigusr.c index d4086ded..4d8a37bc 100644 --- a/programs/tests/t_sigusr.c +++ b/programs/tests/t_sigusr.c @@ -67,8 +67,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - // Pause for a short period before sending the next signal. - sleep(2); + // Pause for a short period (200 ms) before sending the next signal. + timespec_t req = { 0, 200000000 }; + nanosleep(&req, NULL); // Send SIGUSR2 signal to the current process. if (kill(getpid(), SIGUSR2) == -1) { diff --git a/programs/tests/t_sleep.c b/programs/tests/t_sleep.c index cda259b7..455f7b69 100644 --- a/programs/tests/t_sleep.c +++ b/programs/tests/t_sleep.c @@ -8,22 +8,20 @@ #include #include #include +#include #include #include #include int main(int argc, char *argv[]) { - printf("Sleeping for 4 seconds... "); + // Sleep for 500 ms. + struct timespec req = { 0, 500000000 }; // 500 ms = 500,000,000 nanoseconds - // Pause the program execution for 4 seconds. Error checking is included to - // ensure that sleep is successful. - if (sleep(4) != 0) { - perror("sleep"); + if (nanosleep(&req, NULL) != 0) { + fprintf(stderr, "nanosleep error: %s\n", strerror(errno)); return EXIT_FAILURE; } - printf("COMPLETED.\n"); - return EXIT_SUCCESS; } diff --git a/programs/tests/t_spwd.c b/programs/tests/t_spwd.c index f42e7000..6344bbc9 100644 --- a/programs/tests/t_spwd.c +++ b/programs/tests/t_spwd.c @@ -5,6 +5,7 @@ /// See LICENSE.md for details. #include +#include #include #include #include @@ -13,70 +14,73 @@ #include -int main(int argc, char *argv[]) +/// @brief Generates a SHA-256 hash for a predefined input string. +/// @return int EXIT_SUCCESS on success, EXIT_FAILURE on error. +int test_generate(void) { - // Check if the correct number of arguments is provided. - if (argc != 3) { - printf("You can either:\n"); - printf(" -g, --generate : prints the hashed key.\n"); - printf(" -l, --load : prints the hashed key stored for the given user.\n"); - return EXIT_FAILURE; + // Buffer to hold the hashed output. + unsigned char buffer[SHA256_BLOCK_SIZE] = { 0 }; + + // Input string to be hashed. + const char input[] = "Knowledge is power, but enthusiasm pulls the switch."; + + // Buffer to hold the hexadecimal representation of the hash. + char output[SHA256_BLOCK_SIZE * 2 + 1] = { 0 }; + + // Input string to be hashed. + const char expected[] = "6a1399bdcf1fa1ced3d7148a3f5472a5105ff30f730069fc8bdb73bdc018cb42"; + + // SHA-256 context. + SHA256_ctx_t ctx; + + // Initialize the SHA-256 context. + sha256_init(&ctx); + + // Perform the hashing operation 100 times for security. + for (unsigned i = 0; i < 100; ++i) { + sha256_update(&ctx, (unsigned char *)input, strlen(input)); } - // Generate SHA-256 hash for the provided key. - if (!strcmp(argv[1], "--generate") || !strcmp(argv[1], "-g")) { - // Buffer to hold the hashed output. - unsigned char buffer[SHA256_BLOCK_SIZE] = { 0 }; - // Buffer to hold the hexadecimal representation of the hash. - char output[SHA256_BLOCK_SIZE * 2 + 1] = { 0 }; - // SHA-256 context. - SHA256_ctx_t ctx; + // Finalize the hashing. + sha256_final(&ctx, buffer); + + // Convert the hash bytes to a hexadecimal string. + sha256_bytes_to_hex(buffer, SHA256_BLOCK_SIZE, output, SHA256_BLOCK_SIZE * 2 + 1); + + // Check if both hashes match. + if (strncmp(output, expected, strlen(expected))) { + fprintf(stderr, "Hashes do not match:\n"); + fprintf(stderr, "Input : `%s`\n", input); + fprintf(stderr, "Output : `%s`\n", output); + fprintf(stderr, "Expected : `%s`\n", expected); + return EXIT_FAILURE; + } - // Initialize the SHA-256 context. - sha256_init(&ctx); + return EXIT_SUCCESS; +} - // Perform the hashing operation 100,000 times for security. - for (unsigned i = 0; i < 100000; ++i) { - sha256_update(&ctx, (unsigned char *)argv[2], strlen(argv[2])); - } +int test_getspnam(void) +{ + const char *username = "root"; - // Finalize the hashing. - sha256_final(&ctx, buffer); + // Retrieve the shadow password entry for the user. + struct spwd *spbuf = getspnam(username); - // Convert the hash bytes to a hexadecimal string. - sha256_bytes_to_hex(buffer, SHA256_BLOCK_SIZE, output, SHA256_BLOCK_SIZE * 2 + 1); + // Check if the user exists in the shadow password database. + if (!spbuf) { + fprintf(stderr, "Failed to find user '%s' in the shadow password database.\n", username); + return EXIT_FAILURE; + } - // Print the hashed key. - printf("%s\n", output); + return EXIT_SUCCESS; +} +int main(int argc, char *argv[]) +{ + if (test_generate() == EXIT_FAILURE) { + return EXIT_FAILURE; } - // Load and display information for the specified user from the shadow password database. - else if (!strcmp(argv[1], "--load") || !strcmp(argv[1], "-l")) { - // Retrieve the shadow password entry for the user. - struct spwd *spbuf = getspnam(argv[2]); - - // Check if the user exists in the shadow password database. - if (spbuf) { - // Retrieve the last change time. - time_t lstchg = (time_t)spbuf->sp_lstchg; - // Convert to local time structure. - tm_t *tm = localtime(&lstchg); - - // Print the user information (month is zero-based). - printf("name : %s\n", spbuf->sp_namp); - printf("password :\n%s\n", spbuf->sp_pwdp); - printf("lastchange : %02d/%02d %02d:%02d\n", tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - printf("days allowed : %d\n", spbuf->sp_min); - printf("days req. : %d\n", spbuf->sp_max); - printf("days warning : %d\n", spbuf->sp_warn); - printf("days inact : %d\n", spbuf->sp_inact); - printf("days expire : %d\n", spbuf->sp_expire); - } else { - printf("User '%s' not found in the shadow password database.\n", argv[2]); - return EXIT_FAILURE; - } - } else { - printf("Invalid option. Use -g/--generate or -l/--load.\n"); + if (test_getspnam() == EXIT_FAILURE) { return EXIT_FAILURE; } diff --git a/programs/tests/t_stopcont.c b/programs/tests/t_stopcont.c index 373d86cd..2d094f37 100644 --- a/programs/tests/t_stopcont.c +++ b/programs/tests/t_stopcont.c @@ -38,15 +38,21 @@ int main(int argc, char *argv[]) printf("Child process (PID: %d) started.\n", getpid()); + // Sleep for 100 ms. + timespec_t req = { 0, 100000000 }; + while (1) { printf("Child process running...\n"); - sleep(1); + nanosleep(&req, NULL); } } else { // Parent process. + // Sleep for 300 ms. + timespec_t req = { 0, 300000000 }; + // Let the child process run for a bit. - sleep(3); + nanosleep(&req, NULL); if (kill(pid, SIGSTOP) == -1) { perror("failed to send SIGSTOP"); exit(EXIT_FAILURE); @@ -54,7 +60,7 @@ int main(int argc, char *argv[]) printf("Parent sending SIGSTOP to child (PID: %d).\n", pid); // Wait for a bit before continuing the child process. - sleep(3); + nanosleep(&req, NULL); if (kill(pid, SIGCONT) == -1) { perror("failed to send SIGCONT"); exit(EXIT_FAILURE); @@ -62,7 +68,7 @@ int main(int argc, char *argv[]) printf("Parent sending SIGCONT to child (PID: %d).\n", pid); // Wait for a bit before terminating the child process. - sleep(3); + nanosleep(&req, NULL); if (kill(pid, SIGTERM) == -1) { perror("failed to send SIGTERM"); exit(EXIT_FAILURE); diff --git a/programs/tests/t_syslog.c b/programs/tests/t_syslog.c new file mode 100644 index 00000000..8d9e7e82 --- /dev/null +++ b/programs/tests/t_syslog.c @@ -0,0 +1,26 @@ +#include +#include + +int main(void) +{ + // Open syslog connection with identifier "syslog_test" + openlog("syslog_test", LOG_CONS | LOG_PID, LOG_USER); + + // Set log mask to allow only messages of priority LOG_WARNING and above + setlogmask(LOG_UPTO(LOG_WARNING)); + + // Log messages at different levels to test filtering + syslog(LOG_DEBUG, "This is a debug message and should not appear.\n"); + syslog(LOG_INFO, "This is an info message and should not appear.\n"); + syslog(LOG_NOTICE, "This is a notice message and should not appear.\n"); + syslog(LOG_WARNING, "This is a warning message and should appear.\n"); + syslog(LOG_ERR, "This is an error message and should appear.\n"); + syslog(LOG_CRIT, "This is a critical message and should appear.\n"); + syslog(LOG_ALERT, "This is an alert message and should appear.\n"); + syslog(LOG_EMERG, "This is an emergency message and should appear.\n"); + + // Close the syslog connection + closelog(); + + return 0; +}