diff --git a/doc/syscall.md b/doc/syscall.md index b5a30bb..bad40c1 100644 --- a/doc/syscall.md +++ b/doc/syscall.md @@ -52,7 +52,7 @@ If the column is empty it means that it's not implemented yet. | 38 | sys_rename | | const char * | const char * | - | - | - | 2 | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | 2 | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | - | 41 | sys_dup | | unsigned int | - | - | - | - | + 1 | 41 | sys_dup | fs/vfs.c | unsigned int | - | - | - | - | | 42 | sys_pipe | | unsigned long * | - | - | - | - | | 43 | sys_times | | struct tms * | - | - | - | - | 2 | 45 | sys_brk | mem/kheap.c | unsigned long | - | - | - | - | diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 0e73194..b0a247d 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -5,6 +5,7 @@ # Add the library. add_library( libc + ${CMAKE_SOURCE_DIR}/libc/src/unistd/dup.c ${CMAKE_SOURCE_DIR}/libc/src/stdio.c ${CMAKE_SOURCE_DIR}/libc/src/ctype.c ${CMAKE_SOURCE_DIR}/libc/src/string.c diff --git a/libc/inc/sys/unistd.h b/libc/inc/sys/unistd.h index c75ff10..83a87e3 100644 --- a/libc/inc/sys/unistd.h +++ b/libc/inc/sys/unistd.h @@ -253,6 +253,12 @@ int fchdir(int fd); /// appropriately. ssize_t getdents(int fd, dirent_t *dirp, unsigned int count); +/// @brief Return a new file descriptor +/// @param fd The fd pointing to the opened file. +/// @return On success, a new file descriptor is returned. +/// On error, -1 is returned, and errno is set appropriately. +int dup(int fd); + /// @brief Send signal to calling thread after desired seconds. /// @param seconds the amount of seconds. /// @return If there is a previous alarm() request with time remaining, alarm() diff --git a/libc/src/unistd/dup.c b/libc/src/unistd/dup.c new file mode 100644 index 0000000..448cb36 --- /dev/null +++ b/libc/src/unistd/dup.c @@ -0,0 +1,10 @@ +/// @file dup.c +/// @brief +/// @copyright (c) 2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, dup, int, fd) diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index fb1f448..74c360c 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -187,3 +187,11 @@ int vfs_dup_task(struct task_struct *new_task, struct task_struct *old_task); /// @param task The task for which we destroy the file descriptor list. /// @return 0 on fail, 1 on success. int vfs_destroy_task(struct task_struct *task); + +/// @brief Find the smallest available fd. +/// @return -errno on fail, fd on success. +int get_unused_fd(void); + +/// @brief Return new smallest available file desriptor. +/// @return -errno on fail, fd on success. +int sys_dup(int fd); diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 35a179f..d4eaf58 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -604,3 +604,65 @@ int vfs_destroy_task(task_struct *task) } return 1; } + +int get_unused_fd(void) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Search for an unused fd. + int fd; + for (fd = 0; fd < task->max_fd; ++fd) { + if (!task->fd_list[fd].file_struct) { + break; + } + } + + // Check if there is not fd available. + if (fd >= MAX_OPEN_FD) { + return -EMFILE; + } + + // If fd limit is reached, try to allocate more + if (fd == task->max_fd) { + if (!vfs_extend_task_fd_list(task)) { + pr_err("Failed to extend the file descriptor list.\n"); + return -EMFILE; + } + } + + return fd; +} + +int sys_dup(int fd) +{ + // 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]; + vfs_file_t *file = vfd->file_struct; + + // Check the file. + if (file == NULL) { + return -ENOSYS; + } + + fd = get_unused_fd(); + if (fd < 0) + return fd; + + // Increment file reference counter. + file->count += 1; + + // Install the new fd + task->fd_list[fd].file_struct = file; + task->fd_list[fd].flags_mask = vfd->flags_mask; + + return fd; +} diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index 9148635..c7d79c1 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -13,6 +13,7 @@ #include "descriptor_tables/isr.h" #include "devices/fpu.h" +#include "fs/vfs.h" #include "fs/ioctl.h" #include "hardware/timer.h" #include "kernel.h" @@ -91,7 +92,7 @@ void syscall_init(void) sys_call_table[__NR_rename] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; - sys_call_table[__NR_dup] = (SystemCall)sys_ni_syscall; + sys_call_table[__NR_dup] = (SystemCall)sys_dup; sys_call_table[__NR_pipe] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_times] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_brk] = (SystemCall)sys_brk; diff --git a/programs/tests/CMakeLists.txt b/programs/tests/CMakeLists.txt index 7d7635e..fcce458 100644 --- a/programs/tests/CMakeLists.txt +++ b/programs/tests/CMakeLists.txt @@ -1,5 +1,6 @@ # List of programs. set(TEST_LIST + t_dup.c t_creat.c t_write_read.c t_gid.c diff --git a/programs/tests/t_dup.c b/programs/tests/t_dup.c new file mode 100644 index 0000000..9681e1e --- /dev/null +++ b/programs/tests/t_dup.c @@ -0,0 +1,64 @@ +/// @file t_dup.c +/// @brief Test the dup syscall +/// @copyright (c) 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[]) +{ + char *file = "t_dup_file"; + int fd1, fd2; + int flags = O_WRONLY | O_CREAT | O_TRUNC; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + fd1 = open(file, flags, mode); + if (fd1 < 0) { + printf("Failed to open file %s: %s\n", file, strerror(errno)); + exit(1); + } + + fd2 = dup(fd1); + if (fd2 < 0) { + printf("Failed to dup fd %d: %s\n", fd1, strerror(errno)); + exit(1); + } + + if (write(fd1, "foo", 3) != 3) { + printf("Writing to fd %d failed: %s\n", fd1, strerror(errno)); + exit(1); + } + close(fd1); + + if (write(fd2, "bar", 3) != 3) { + printf("Writing to fd %d failed: %s\n", fd2, strerror(errno)); + exit(1); + } + close(fd2); + + fd1 = open(file, O_RDONLY, 0); + if (fd1 < 0) { + printf("Failed to open file %s: %s\n", file, strerror(errno)); + exit(1); + } + + char buf[7]; + buf[6] = 0; + if (read(fd1, &buf, 6) < 0) { + printf("Reading from fd %d failed: %s\n", fd1, strerror(errno)); + exit(1); + } + + if (strcmp(buf, "foobar") != 0) { + printf("Unexpected file content: %s\n", buf); + exit(1); + } + + unlink(file); + return 0; +}