From f066dd2ad9926d8643d52db21c370bb694043146 Mon Sep 17 00:00:00 2001 From: "Enrico Fraccaroli (Galfurian)" Date: Tue, 21 Nov 2023 14:20:41 -0500 Subject: [PATCH] Add support for reading symlinks --- files/home/user/README | 1 + libc/CMakeLists.txt | 2 ++ libc/inc/limits.h | 3 +++ libc/inc/sys/unistd.h | 7 +++++ libc/src/grp.c | 35 ++++++++++++------------ libc/src/unistd/readlink.c | 10 +++++++ libc/src/unistd/symlink.c | 2 +- mentos/inc/fs/vfs.h | 7 +++++ mentos/inc/fs/vfs_types.h | 4 +++ mentos/inc/system/syscall.h | 7 +++++ mentos/src/drivers/ata.c | 3 ++- mentos/src/fs/ext2.c | 37 +++++++++++++++++++++---- mentos/src/fs/namei.c | 18 ++++++++++++- mentos/src/fs/procfs.c | 3 ++- mentos/src/fs/vfs.c | 14 ++++++++++ mentos/src/io/proc_feedback.c | 3 ++- mentos/src/io/proc_ipc.c | 9 ++++--- mentos/src/io/proc_running.c | 3 ++- mentos/src/io/proc_system.c | 3 ++- mentos/src/io/proc_video.c | 3 ++- mentos/src/system/syscall.c | 2 +- programs/cat.c | 51 ++++++++++++++++++++++------------- 22 files changed, 173 insertions(+), 54 deletions(-) create mode 120000 files/home/user/README create mode 100644 libc/src/unistd/readlink.c diff --git a/files/home/user/README b/files/home/user/README new file mode 120000 index 0000000..3830a41 --- /dev/null +++ b/files/home/user/README @@ -0,0 +1 @@ +../../README \ No newline at end of file diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 1363c49..3c96674 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -133,6 +133,8 @@ add_library( ${PROJECT_SOURCE_DIR}/src/unistd/kill.c ${PROJECT_SOURCE_DIR}/src/unistd/signal.c ${PROJECT_SOURCE_DIR}/src/unistd/interval.c + ${PROJECT_SOURCE_DIR}/src/unistd/symlink.c + ${PROJECT_SOURCE_DIR}/src/unistd/readlink.c ${PROJECT_SOURCE_DIR}/src/libc_start.c ${PROJECT_SOURCE_DIR}/src/crt0.S ) diff --git a/libc/inc/limits.h b/libc/inc/limits.h index 6a18610..1d387e1 100644 --- a/libc/inc/limits.h +++ b/libc/inc/limits.h @@ -53,5 +53,8 @@ /// Maximum number of characters in a path name. #define PATH_MAX 4096 +/// Maximum length of arguments provided to exec function. +#define ARG_MAX 256 + /// Maximum pid number. #define PID_MAX_LIMIT 32768 diff --git a/libc/inc/sys/unistd.h b/libc/inc/sys/unistd.h index 348b196..c929f90 100644 --- a/libc/inc/sys/unistd.h +++ b/libc/inc/sys/unistd.h @@ -60,6 +60,13 @@ int unlink(const char *path); /// @return 0 on success, a negative number if fails and errno is set. int symlink(const char *linkname, const char *path); +/// @brief Read the symbolic link, if present. +/// @param file the file for which we want to read the symbolic link information. +/// @param buffer the buffer where we will store the symbolic link path. +/// @param bufsize the size of the buffer. +/// @return The number of read characters on success, -1 otherwise and errno is set to indicate the error. +int readlink(const char *path, char *buffer, size_t bufsize); + /// @brief Wrapper for exit system call. /// @param status The exit status. extern void exit(int status); diff --git a/libc/src/grp.c b/libc/src/grp.c index 06216e1..d9451c3 100644 --- a/libc/src/grp.c +++ b/libc/src/grp.c @@ -18,30 +18,31 @@ static int __fd = -1; /// @brief It parses the line (as string) and saves its content inside the /// group_t structure. /// @param grp the struct where we store the information. -/// @param buf the buffer from which we extract the information. +/// @param buf the line from which we extract the information. static inline void __parse_line(group_t *grp, char *buf) { assert(grp && "Received null grp!"); char *token; // Parse the group name. - if ((token = strtok(buf, ":")) != NULL) { + token = strtok(buf, ":"); + if (token != NULL) { grp->gr_name = token; } // Parse the group passwd. - if ((token = strtok(NULL, ":")) != NULL) { + token = strtok(NULL, ":"); + if (token != NULL) { grp->gr_passwd = token; } // Parse the group id. - if ((token = strtok(NULL, ":")) != NULL) { + token = strtok(NULL, ":"); + if (token != NULL) { grp->gr_gid = atoi(token); } - size_t found_users = 0; while ((token = strtok(NULL, ",\n\0")) != NULL && found_users < MAX_MEMBERS_PER_GROUP) { grp->gr_mem[found_users] = token; found_users += 1; } - // Null terminate array grp->gr_mem[found_users] = "\0"; } @@ -52,8 +53,8 @@ static inline void __parse_line(group_t *grp, char *buf) /// @param buflen the length of the buffer. /// @param name the name we are looking for. /// @param gid the group id we must match. -/// @return a pointer to the filled input buffer on success, NULL on failure. -static inline char *__search_entry(int fd, char *buf, int buflen, const char *name, gid_t gid) +/// @return 1 on success, 0 on failure. +static inline int __search_entry(int fd, char *buf, size_t buflen, const char *name, gid_t gid) { int ret; char c; @@ -65,7 +66,7 @@ static inline char *__search_entry(int fd, char *buf, int buflen, const char *na } if (pos >= buflen) { errno = ERANGE; - return NULL; + return 0; } // If we have found a newline or the EOF, parse the entry. if ((c == '\n') || (ret == EOF)) { @@ -74,7 +75,7 @@ static inline char *__search_entry(int fd, char *buf, int buflen, const char *na // Check the entry. if (name) { if (strncmp(buf, name, strlen(name)) == 0) { - return buf; + return 1; } } else { int gid_start = -1, col_count = 0; @@ -91,7 +92,7 @@ static inline char *__search_entry(int fd, char *buf, int buflen, const char *na int found_gid = atoi(&buf[gid_start]); // Check the gid. if (found_gid == gid) { - return buf; + return 1; } } } @@ -106,7 +107,7 @@ static inline char *__search_entry(int fd, char *buf, int buflen, const char *na } } errno = ENOENT; - return NULL; + return 0; } group_t *getgrgid(gid_t gid) @@ -148,12 +149,11 @@ int getgrgid_r(gid_t gid, group_t *group, char *buf, size_t buflen, group_t **re return 0; } - char *entry = __search_entry(fd, buf, buflen, NULL, gid); - if (entry != NULL) { + if (__search_entry(fd, buf, buflen, NULL, gid)) { // Close the file. close(fd); // Parse the line. - __parse_line(group, entry); + __parse_line(group, buf); // Return success. return 1; } @@ -173,12 +173,11 @@ int getgrnam_r(const char *name, group_t *group, char *buf, size_t buflen, group return 0; } - char *entry = __search_entry(fd, buf, buflen, name, 0); - if (entry != NULL) { + if (__search_entry(fd, buf, buflen, name, 0)) { // Close the file. close(fd); // Parse the line. - __parse_line(group, entry); + __parse_line(group, buf); // Return success. return 1; } diff --git a/libc/src/unistd/readlink.c b/libc/src/unistd/readlink.c new file mode 100644 index 0000000..0dc2eb9 --- /dev/null +++ b/libc/src/unistd/readlink.c @@ -0,0 +1,10 @@ +/// @file readlink.c +/// @brief +/// @copyright (c) 2014-2023 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "sys/errno.h" +#include "system/syscall_types.h" + +_syscall3(int, readlink, const char *, path, char *, buffer, size_t, bufsize) diff --git a/libc/src/unistd/symlink.c b/libc/src/unistd/symlink.c index 1ed4cc5..9744e79 100644 --- a/libc/src/unistd/symlink.c +++ b/libc/src/unistd/symlink.c @@ -1,4 +1,4 @@ -/// @file unlink.c +/// @file symlink.c /// @brief /// @copyright (c) 2014-2023 This file is distributed under the MIT License. /// See LICENSE.md for details. diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index a07c9cb..1dae853 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -119,6 +119,13 @@ int vfs_rmdir(const char *path); /// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) vfs_file_t *vfs_creat(const char *path, mode_t mode); +/// @brief Read the symbolic link, if present. +/// @param file the file for which we want to read the symbolic link information. +/// @param buffer the buffer where we will store the symbolic link path. +/// @param bufsize the size of the buffer. +/// @return The number of read characters on success, -1 otherwise and errno is set to indicate the error. +ssize_t vfs_readlink(vfs_file_t *file, char *buffer, size_t bufsize); + /// @brief Creates a symbolic link. /// @param linkname the name of the link. /// @param path the entity it is linking to. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index c00f230..f7771c2 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -46,6 +46,8 @@ typedef int (*vfs_fstat_callback)(vfs_file_t *, stat_t *); 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)(vfs_file_t *, char *, size_t); /// @brief Filesystem information. typedef struct file_system_type { @@ -91,6 +93,8 @@ typedef struct vfs_file_operations_t { 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; } vfs_file_operations_t; /// @brief Data structure that contains information about the mounted filesystems. diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index dd00328..fb73a1a 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -217,6 +217,13 @@ int sys_rmdir(const char *path); /// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) int sys_creat(const char *path, mode_t mode); +/// @brief Read the symbolic link, if present. +/// @param file the file for which we want to read the symbolic link information. +/// @param buffer the buffer where we will store the symbolic link path. +/// @param bufsize the size of the buffer. +/// @return The number of read characters on success, -1 otherwise and errno is set to indicate the error. +int sys_readlink(const char *path, char *buffer, size_t bufsize); + /// @brief Creates a symbolic link. /// @param linkname the name of the link. /// @param path the entity it is linking to. diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index c2df4b6..57a01b3 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -1131,7 +1131,8 @@ static vfs_file_operations_t ata_fs_operations = { .lseek_f = NULL, .stat_f = ata_fstat, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; static vfs_file_t *ata_device_create(ata_device_t *dev) diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index 73ed1a9..7a9af1a 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.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__ "[EXT2 ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[EXT2 ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "fcntl.h" @@ -378,6 +378,7 @@ 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 ssize_t ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count); +static ssize_t ext2_readlink(vfs_file_t *file, char *buffer, size_t bufsize); static int ext2_mkdir(const char *path, mode_t mode); static int ext2_rmdir(const char *path); @@ -408,7 +409,8 @@ static vfs_file_operations_t ext2_fs_operations = { .lseek_f = ext2_lseek, .stat_f = ext2_fstat, .ioctl_f = ext2_ioctl, - .getdents_f = ext2_getdents + .getdents_f = ext2_getdents, + .readlink_f = ext2_readlink, }; // ============================================================================ @@ -2393,6 +2395,9 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) errno = ENOENT; return NULL; } + if (direntry.file_type == ext2_file_type_symbolic_link) { + pr_alert("Beware, it is a symbolic link.\n"); + } // Prepare the structure for the inode. ext2_inode_t inode; memset(&inode, 0, sizeof(ext2_inode_t)); @@ -2737,6 +2742,28 @@ static ssize_t ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_ return written; } +static ssize_t ext2_readlink(vfs_file_t *file, char *buffer, size_t bufsize) +{ + // Get the filesystem. + 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 -ENOENT; + } + // Get the inode associated with the file. + ext2_inode_t inode; + if (ext2_read_inode(fs, &inode, file->ino) == -1) { + pr_err("Failed to read the inode (%d).\n", file->ino); + return -ENOENT; + } + // Get the length of the symlink. + ssize_t nbytes = min(strlen(inode.data.symlink), bufsize); + // Copy the symlink information. + strncpy(buffer, inode.data.symlink, nbytes); + // Return how much we read. + return nbytes; +} + static int ext2_mkdir(const char *path, mode_t permission) { pr_debug("\next2_mkdir(%s, %d)\n", path, permission); diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c index 56fe750..4d01d2b 100644 --- a/mentos/src/fs/namei.c +++ b/mentos/src/fs/namei.c @@ -3,9 +3,10 @@ /// @copyright (c) 2014-2023 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "process/scheduler.h" +#include "fcntl.h" #include "fs/vfs.h" #include "io/debug.h" +#include "process/scheduler.h" #include "sys/errno.h" int sys_unlink(const char *path) @@ -66,3 +67,18 @@ int sys_symlink(const char *linkname, const char *path) { return vfs_symlink(linkname, path); } + +int sys_readlink(const char *path, char *buffer, size_t bufsize) +{ + // Try to open the file. + vfs_file_t *file = vfs_open(path, O_RDONLY, 0); + if (file == NULL) { + return -errno; + } + // Read the link. + ssize_t nbytes = vfs_readlink(file, buffer, bufsize); + // Close the file. + vfs_close(file); + // Return the number of bytes we read. + return nbytes; +} diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c index 932ec15..3dbac1d 100644 --- a/mentos/src/fs/procfs.c +++ b/mentos/src/fs/procfs.c @@ -115,7 +115,8 @@ static vfs_file_operations_t procfs_fs_operations = { .lseek_f = procfs_lseek, .stat_f = procfs_fstat, .ioctl_f = procfs_ioctl, - .getdents_f = procfs_getdents + .getdents_f = procfs_getdents, + .readlink_f = NULL, }; // ============================================================================ diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 79478e6..e192458 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -331,6 +331,20 @@ vfs_file_t *vfs_creat(const char *path, mode_t mode) return file; } +ssize_t vfs_readlink(vfs_file_t *file, char *buffer, size_t bufsize) +{ + if (file == NULL) { + pr_err("vfs_readlink: received a null pointer for file.\n"); + return -ENOENT; + } + if (file->fs_operations->readlink_f == NULL) { + pr_err("vfs_readlink(%s): Function not supported in current filesystem.", file->name); + return -ENOSYS; + } + // Perform the read. + return file->fs_operations->readlink_f(file, buffer, bufsize); +} + int vfs_symlink(const char *linkname, const char *path) { // Allocate a variable for the path. diff --git a/mentos/src/io/proc_feedback.c b/mentos/src/io/proc_feedback.c index 3e23f1c..b677d0b 100644 --- a/mentos/src/io/proc_feedback.c +++ b/mentos/src/io/proc_feedback.c @@ -40,7 +40,8 @@ static vfs_file_operations_t procfb_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; int procfb_module_init() diff --git a/mentos/src/io/proc_ipc.c b/mentos/src/io/proc_ipc.c index 63721d1..ff0ff5f 100644 --- a/mentos/src/io/proc_ipc.c +++ b/mentos/src/io/proc_ipc.c @@ -37,7 +37,8 @@ static vfs_file_operations_t procipc_msg_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; /// Filesystem file operations for semaphores. @@ -50,7 +51,8 @@ static vfs_file_operations_t procipc_sem_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; /// Filesystem file operations for shared memry. @@ -63,7 +65,8 @@ static vfs_file_operations_t procipc_shm_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; int procipc_module_init() diff --git a/mentos/src/io/proc_running.c b/mentos/src/io/proc_running.c index 63e34f3..46031ed 100644 --- a/mentos/src/io/proc_running.c +++ b/mentos/src/io/proc_running.c @@ -424,7 +424,8 @@ static vfs_file_operations_t procr_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; int procr_create_entry_pid(task_struct *entry) diff --git a/mentos/src/io/proc_system.c b/mentos/src/io/proc_system.c index c5e73aa..ccdbf4a 100644 --- a/mentos/src/io/proc_system.c +++ b/mentos/src/io/proc_system.c @@ -86,7 +86,8 @@ static vfs_file_operations_t procs_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; int procs_module_init() diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c index 11b8a3f..c075c67 100644 --- a/mentos/src/io/proc_video.c +++ b/mentos/src/io/proc_video.c @@ -229,7 +229,8 @@ static vfs_file_operations_t procv_fs_operations = { .lseek_f = NULL, .stat_f = procv_fstat, .ioctl_f = procv_ioctl, - .getdents_f = NULL + .getdents_f = NULL, + .readlink_f = NULL, }; int procv_module_init() diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index 18a7ea1..384c06b 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -125,7 +125,7 @@ void syscall_init() sys_call_table[__NR_setgroups] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_symlink] = (SystemCall)sys_symlink; sys_call_table[__NR_lstat] = (SystemCall)sys_ni_syscall; - sys_call_table[__NR_readlink] = (SystemCall)sys_ni_syscall; + sys_call_table[__NR_readlink] = (SystemCall)sys_readlink; sys_call_table[__NR_uselib] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_swapon] = (SystemCall)sys_ni_syscall; sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; diff --git a/programs/cat.c b/programs/cat.c index 739da34..126e9a7 100644 --- a/programs/cat.c +++ b/programs/cat.c @@ -3,6 +3,8 @@ /// @copyright (c) 2014-2023 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "io/debug.h" +#include "stddef.h" #include #include #include @@ -10,6 +12,22 @@ #include #include +static inline void print_content(const char *path, char *buffer, unsigned buflen) +{ + // Open the file. + int fd = open(path, O_RDONLY, 42); + if (fd >= 0) { + // Put on the standard output the characters. + while (read(fd, buffer, buflen) > 0) { + puts(buffer); + } + // Close the file descriptor. + close(fd); + } else { + printf("cat: %s: %s\n\n", path, strerror(errno)); + } +} + int main(int argc, char **argv) { if (argc < 2) { @@ -30,30 +48,25 @@ int main(int argc, char **argv) char buffer[BUFSIZ]; // Iterate the arguments. for (int i = 1; i < argc; ++i) { - int fd = open(argv[i], O_RDONLY, 42); - if (fd < 0) { - printf("cat: %s: %s\n\n", argv[i], strerror(errno)); - continue; - } stat_t statbuf; - if (fstat(fd, &statbuf) == -1) { + if (stat(argv[i], &statbuf) == -1) { printf("cat: %s: %s\n\n", argv[i], strerror(errno)); - // Close the file descriptor. - close(fd); continue; } - if (S_ISDIR(statbuf.st_mode)) { - printf("cat: %s: %s\n\n", argv[i], strerror(EISDIR)); - // Close the file descriptor. - close(fd); - continue; + // If it is a regular file, just print the content. + if (S_ISREG(statbuf.st_mode)) { + print_content(argv[i], buffer, BUFSIZ); + + } else if (S_ISDIR(statbuf.st_mode)) { + printf("cat: %s: Is a directory\n\n", argv[i]); + + } else if (S_ISLNK(statbuf.st_mode)) { + if (readlink(argv[i], buffer, BUFSIZ)) { + print_content(buffer, buffer, BUFSIZ); + } else { + printf("cat: %s: %s\n\n", argv[i], strerror(errno)); + } } - // Put on the standard output the characters. - while (read(fd, buffer, BUFSIZ) > 0) { - puts(buffer); - } - // Close the file descriptor. - close(fd); } putchar('\n'); putchar('\n');