diff --git a/files/home/user/.bashrc b/files/home/user/.bashrc deleted file mode 100644 index e69de29b..00000000 diff --git a/files/dev/README b/files/home/user/.shellrc similarity index 100% rename from files/dev/README rename to files/home/user/.shellrc diff --git a/files/home/user/tmp.md b/files/home/user/tmp.md new file mode 120000 index 00000000..9b519420 --- /dev/null +++ b/files/home/user/tmp.md @@ -0,0 +1 @@ +../user/welcome.md \ No newline at end of file diff --git a/files/home/user/welcome.md b/files/home/user/welcome.md new file mode 100644 index 00000000..b3572567 --- /dev/null +++ b/files/home/user/welcome.md @@ -0,0 +1,8 @@ +MentOS 0.7.2 + +Welcome to the MentOS, the Mentoring Operating System. + +If you want some help enter the "man" command into the cli. + +Bye, +The Ment(OS) Team diff --git a/files/proc/README b/files/proc/README deleted file mode 100644 index e69de29b..00000000 diff --git a/libc/inc/fcntl.h b/libc/inc/fcntl.h index c3700fd5..991f527b 100644 --- a/libc/inc/fcntl.h +++ b/libc/inc/fcntl.h @@ -15,33 +15,3 @@ #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. - -/// @defgroup ModeBitsAccessPermission Mode Bits for Access Permission -/// @brief The file modes. -/// @{ - -#define S_ISUID 0x0800 ///< Set user id on execution -#define S_ISGID 0x0400 ///< Set group id on execution -#define S_ISVTX 0x0200 ///< Save swapped text even after use (Sticky Bit) -#define S_IRWXU 0x01C0 ///< rwx------ : User can read/write/execute -#define S_IRUSR 0x0100 ///< r-------- : User can read -#define S_IWUSR 0x0080 ///< -w------- : User can write -#define S_IXUSR 0x0040 ///< --x------ : User can execute -#define S_IRWXG 0x0038 ///< ---rwx--- : Group can read/write/execute -#define S_IRGRP 0x0020 ///< ---r----- : Group can read -#define S_IWGRP 0x0010 ///< ----w---- : Group can write -#define S_IXGRP 0x0008 ///< -----x--- : Group can execute -#define S_IRWXO 0x0007 ///< ------rwx : Others can read/write/execute -#define S_IROTH 0x0004 ///< ------r-- : Others can read -#define S_IWOTH 0x0002 ///< -------w- : Others can write -#define S_IXOTH 0x0001 ///< --------x : Others can execute - -#define S_ISDIR(m) (((m)&0170000) == 0040000) ///< directory. -#define S_ISCHR(m) (((m)&0170000) == 0020000) ///< char special -#define S_ISBLK(m) (((m)&0170000) == 0060000) ///< block special -#define S_ISREG(m) (((m)&0170000) == 0100000) ///< regular file -#define S_ISFIFO(m) (((m)&0170000) == 0010000) ///< fifo -#define S_ISLNK(m) (((m)&0170000) == 0120000) ///< symbolic link -#define S_ISSOCK(m) (((m)&0170000) == 0140000) ///< socket - -/// @} diff --git a/libc/inc/string.h b/libc/inc/string.h index 9c24a31c..575ff9e3 100644 --- a/libc/inc/string.h +++ b/libc/inc/string.h @@ -178,7 +178,7 @@ char *strtok_r(char *str, const char *delim, char **saveptr); /// @param buffer the buffer where we save the parsed token. /// @param buflen the length of the buffer. /// @return 1 if we still have things to parse, 0 if we finished parsing. -int tokenize(const char *string, char *separators, size_t *offset, char *buffer, ssize_t buflen); +int tokenize(const char *string, const char *separators, size_t *offset, char *buffer, ssize_t buflen); /// @brief Copies the values of num bytes from the location pointed by source /// to the memory block pointed by destination. diff --git a/libc/inc/sys/stat.h b/libc/inc/sys/stat.h index 3b29421b..691d49f6 100644 --- a/libc/inc/sys/stat.h +++ b/libc/inc/sys/stat.h @@ -10,6 +10,52 @@ #include "stddef.h" #include "time.h" +/// @defgroup FileTypes File Types Macros +/// @brief These constants allow to identify file types. +/// @{ +#define S_IFMT 0xF000 ///< Format mask +#define S_IFSOCK 0xC000 ///< Socket +#define S_IFLNK 0xA000 ///< Symbolic link +#define S_IFREG 0x8000 ///< Regular file +#define S_IFBLK 0x6000 ///< Block device +#define S_IFDIR 0x4000 ///< Directory +#define S_IFCHR 0x2000 ///< Character device +#define S_IFIFO 0x1000 ///< Fifo +/// @} + +/// @defgroup FileTypeTest File Type Test Macros +/// @brief These macros allows to easily identify file types. +/// @{ +#define S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) +#define S_ISSOCK(mode) (S_ISTYPE(mode, S_IFSOCK)) ///< Check if a socket. +#define S_ISLNK(mode) (S_ISTYPE(mode, S_IFLNK)) ///< Check if a symbolic link. +#define S_ISREG(mode) (S_ISTYPE(mode, S_IFREG)) ///< Check if a regular file. +#define S_ISBLK(mode) (S_ISTYPE(mode, S_IFBLK)) ///< Check if a block special. +#define S_ISDIR(mode) (S_ISTYPE(mode, S_IFDIR)) ///< Check if a directory. +#define S_ISCHR(mode) (S_ISTYPE(mode, S_IFCHR)) ///< Check if a char special. +#define S_ISFIFO(mode) (S_ISTYPE(mode, S_IFIFO)) ///< Check if a fifo. +/// @} + +/// @defgroup ModeBitsAccessPermission Mode Bits for Access Permission +/// @brief These constants allow to control access permission for files. +/// @{ +#define S_ISUID 0x0800 ///< Set user id on execution +#define S_ISGID 0x0400 ///< Set group id on execution +#define S_ISVTX 0x0200 ///< Save swapped text even after use (Sticky Bit) +#define S_IRWXU 0x01C0 ///< rwx------ : User can read/write/execute +#define S_IRUSR 0x0100 ///< r-------- : User can read +#define S_IWUSR 0x0080 ///< -w------- : User can write +#define S_IXUSR 0x0040 ///< --x------ : User can execute +#define S_IRWXG 0x0038 ///< ---rwx--- : Group can read/write/execute +#define S_IRGRP 0x0020 ///< ---r----- : Group can read +#define S_IWGRP 0x0010 ///< ----w---- : Group can write +#define S_IXGRP 0x0008 ///< -----x--- : Group can execute +#define S_IRWXO 0x0007 ///< ------rwx : Others can read/write/execute +#define S_IROTH 0x0004 ///< ------r-- : Others can read +#define S_IWOTH 0x0002 ///< -------w- : Others can write +#define S_IXOTH 0x0001 ///< --------x : Others can execute +/// @} + /// @brief Retrieves information about the file at the given location. /// @param path The path to the file that is being inquired. /// @param buf A structure where data about the file will be stored. diff --git a/libc/src/string.c b/libc/src/string.c index 14cc4209..525e7a77 100644 --- a/libc/src/string.c +++ b/libc/src/string.c @@ -5,9 +5,9 @@ #include "string.h" #include "ctype.h" -#include "fcntl.h" #include "stdio.h" #include "stdlib.h" +#include "sys/stat.h" #ifdef __KERNEL__ #include "mem/kheap.h" @@ -218,23 +218,30 @@ char *strpbrk(const char *string, const char *control) return NULL; } -int tokenize(const char *string, char *separators, size_t *offset, char *buffer, ssize_t buflen) +int tokenize(const char *string, const char *separators, size_t *offset, char *buffer, ssize_t buflen) { // If we reached the end of the parsed string, stop. if ((*offset >= buflen) || (string[*offset] == 0)) { return 0; } + // Skip any leading (multiple) separators. + while (string[*offset] != 0 && strchr(separators, string[*offset])) { + ++(*offset); + } + // If we reach the end after skipping, return 0. + if (string[*offset] == 0) { + return 0; + } // Keep copying character until we either reach 1) the end of the buffer, 2) a // separator, or 3) the end of the string we are parsing. do { - for (char *separator = separators; *separator != 0; ++separator) { - if (string[*offset] == *separator) { - // Skip the character. - ++(*offset); - // Close the buffer. - *buffer = '\0'; - return 1; - } + // Check if the character is a separator. + if (strchr(separators, string[*offset])) { + // Skip the character. + ++(*offset); + // Close the buffer. + *buffer = '\0'; + return 1; } // Save the character. *buffer = string[*offset]; @@ -496,7 +503,7 @@ size_t strlen(const char *s) size_t strnlen(const char *s, size_t count) { const char *p = memchr(s, 0, count); - return p ? (size_t)(p-s) : count; + return p ? (size_t)(p - s) : count; } int strcmp(const char *s1, const char *s2) diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index b84003c4..4faef2b5 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -19,11 +19,6 @@ extern kmem_cache_t *vfs_file_cache; /// @brief Forward declaration of task_struct. struct task_struct; -/// @brief Searches for the mountpoint of the given path. -/// @param absolute_path Path for which we want to search the mountpoint. -/// @return Pointer to the vfs_file of the mountpoint. -super_block_t *vfs_get_superblock(const char *absolute_path); - /// @brief Initialize the Virtual File System (VFS). void vfs_init(void); @@ -37,6 +32,15 @@ int vfs_register_filesystem(file_system_type *fs); /// @return The outcome of the operation, 0 if fails. int vfs_unregister_filesystem(file_system_type *fs); +int vfs_register_superblock(const char *name, const char *path, file_system_type *type, vfs_file_t *root); + +int vfs_unregister_superblock(super_block_t *sb); + +/// @brief Searches for the mountpoint of the given path. +/// @param absolute_path Path for which we want to search the mountpoint. +/// @return Pointer to the vfs_file of the mountpoint. +super_block_t *vfs_get_superblock(const char *absolute_path); + /// @brief Given an absolute path to a file, vfs_open_abspath() returns a file struct, used to access the file. /// @param absolute_path An absolute path to a file. /// @param flags Used to set the file status flags and file access modes of the open file description. @@ -127,11 +131,11 @@ int vfs_rmdir(const char *path); 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 path the path to the symbolic link. /// @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); +ssize_t vfs_readlink(const char *path, char *buffer, size_t bufsize); /// @brief Creates a symbolic link. /// @param linkname the name of the link. @@ -151,24 +155,12 @@ int vfs_stat(const char *path, stat_t *buf); /// @return 0 on success, -errno on failure. int vfs_fstat(vfs_file_t *file, stat_t *buf); -/// @brief Mount a file system to the specified path. -/// @param path Path where we want to map the filesystem. -/// @param fs_root Root node of the filesystem. -/// @return 1 on success, 0 on fail. -/// @details -/// For example, if we have an EXT2 filesystem with a root node -/// of ext2_root and we want to mount it to /, we would run -/// vfs_mount("/", ext2_root); - or, if we have a procfs node, -/// we could mount that to /dev/procfs. Individual files can also -/// be mounted. -int vfs_mount(const char *path, vfs_file_t *fs_root); - /// @brief Mount the path as a filesystem of the given type. /// @param type The type of filesystem /// @param path The path to where it should be mounter. /// @param args The arguments passed to the filesystem mount callback. /// @return 0 on success, a negative number if fails and errno is set. -int do_mount(const char *type, const char *path, const char *args); +int vfs_mount(const char *type, const char *path, const char *args); /// @brief Locks the access to the given file. /// @param file The file to lock. @@ -199,7 +191,6 @@ int vfs_destroy_task(struct task_struct *task); /// @return -errno on fail, fd on success. int get_unused_fd(void); - /// @brief Return new smallest available file desriptor. /// @param fd the descriptor of the file we want to duplicate. /// @return -errno on fail, fd on success. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index 2a46a03b..1d6a0d82 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -50,7 +50,7 @@ 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); +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. diff --git a/mentos/inc/version.h b/mentos/inc/version.h index 042f8a6b..5b8be03d 100644 --- a/mentos/inc/version.h +++ b/mentos/inc/version.h @@ -21,7 +21,7 @@ #define OS_MINOR_VERSION 7 /// Micro version of the operating system. -#define OS_MICRO_VERSION 2 +#define OS_MICRO_VERSION 3 /// Helper to transform the given argument into a string. #define OS_STR_HELPER(x) #x diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index bca28c74..741382a1 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -1172,6 +1172,25 @@ static int ata_stat(const char *path, stat_t *stat) } // == VFS ENTRY GENERATION ==================================================== + +/// @brief The mount call-back, which prepares everything and calls the actual +/// ATA mount function. +/// @param path the path where the filesystem should be mounted. +/// @param device the device we mount. +/// @return the VFS file of the filesystem. +static vfs_file_t *ata_mount_callback(const char *path, const char *device) +{ + pr_err("mount_callback(%s, %s): ATA has no mount callback!\n", path, device); + return NULL; +} + +/// Filesystem information. +static file_system_type ata_file_system_type = { + .name = "ata", + .fs_flags = 0, + .mount = ata_mount_callback +}; + /// Filesystem general operations. static vfs_sys_operations_t ata_sys_operations = { .mkdir_f = NULL, @@ -1208,9 +1227,9 @@ static vfs_file_t *ata_device_create(ata_device_t *dev) } // Set the device name. memcpy(file->name, dev->name, NAME_MAX); - file->uid = 0; - file->gid = 0; - file->mask = 0x2000 | 0600; + file->uid = 0; + file->gid = 0; + file->mask = 0x2000 | 0600; file->atime = sys_time(NULL); file->mtime = sys_time(NULL); file->ctime = sys_time(NULL); @@ -1259,7 +1278,7 @@ static ata_device_type_t ata_device_detect(ata_device_t *dev) // Update the filesystem entry with the length of the device. dev->fs_root->length = ata_max_offset(dev); // Try to mount the drive. - if (!vfs_mount(dev->path, dev->fs_root)) { + if (!vfs_register_superblock(dev->fs_root->name, dev->path, &ata_file_system_type, dev->fs_root)) { pr_alert("Failed to mount ata device!\n"); // Free the memory. kmem_cache_free(dev->fs_root); @@ -1273,12 +1292,6 @@ static ata_device_type_t ata_device_detect(ata_device_t *dev) } else if (type == ata_dev_type_no_device) { pr_debug("[%s] Found no device...\n", ata_get_device_settings_str(dev)); } - // if (type == ata_dev_type_unknown) { - // pr_debug("[%s] Found unsupported device...\n", ata_get_device_settings_str(dev)); - // } - // if ((type != ata_dev_type_no_device) && (type != ata_dev_type_unknown)) { - // pr_warning(" Found %s device connected to %s.\n", ata_get_device_type_str(type), ata_get_device_settings_str(dev)); - // } return type; } @@ -1320,11 +1333,15 @@ static void pci_find_ata(uint32_t device, uint16_t vendorid, uint16_t deviceid, } // == INITIALIZE/FINALIZE ATA ================================================= + int ata_initialize(void) { // Search for ATA devices. pci_scan(&pci_find_ata, -1, &ata_pci); + // Register the filesystem. + vfs_register_filesystem(&ata_file_system_type); + // Install the IRQ handlers. irq_install_handler(IRQ_FIRST_HD, ata_irq_handler_master, "IDE Master"); irq_install_handler(IRQ_SECOND_HD, ata_irq_handler_slave, "IDE Slave"); diff --git a/mentos/src/drivers/mem.c b/mentos/src/drivers/mem.c index d57063f2..b64e3d98 100644 --- a/mentos/src/drivers/mem.c +++ b/mentos/src/drivers/mem.c @@ -27,9 +27,10 @@ struct memdev { static struct memdev *devices; -static void add_device(struct memdev *device) { +static void add_device(struct memdev *device) +{ struct memdev *dit = devices; - for (;dit != NULL; dit = dit->next); + for (; dit != NULL; dit = dit->next); if (dit == NULL) { devices = device; } else { @@ -37,8 +38,9 @@ static void add_device(struct memdev *device) { } } -static vfs_file_t* find_device_file(const char *path) { - for(struct memdev *dev = devices; dev != NULL; dev = dev->next) { +static vfs_file_t *find_device_file(const char *path) +{ + for (struct memdev *dev = devices; dev != NULL; dev = dev->next) { if (strcmp(dev->file->name, path) == 0) { return dev->file; } @@ -46,7 +48,7 @@ static vfs_file_t* find_device_file(const char *path) { return NULL; } -static int mem_stat(const char* path, stat_t *stat); +static int mem_stat(const char *path, stat_t *stat); static vfs_sys_operations_t mem_sys_operations = { .mkdir_f = NULL, @@ -55,10 +57,28 @@ static vfs_sys_operations_t mem_sys_operations = { }; static vfs_file_t *null_open(const char *path, int flags, mode_t mode); -static int null_close(vfs_file_t * file); -static ssize_t null_write(vfs_file_t * file, const void *buffer, off_t offset, size_t size); -static ssize_t null_read(vfs_file_t * file, char *buffer, off_t offset, size_t size); -static int null_fstat(vfs_file_t * file, stat_t *stat); +static int null_close(vfs_file_t *file); +static ssize_t null_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size); +static ssize_t null_read(vfs_file_t *file, char *buffer, off_t offset, size_t size); +static int null_fstat(vfs_file_t *file, stat_t *stat); + +/// @brief The mount call-back, which prepares everything and calls the actual +/// NULL mount function. +/// @param path the path where the filesystem should be mounted. +/// @param device the device we mount. +/// @return the VFS file of the filesystem. +static vfs_file_t *null_mount_callback(const char *path, const char *device) +{ + pr_err("mount_callback(%s, %s): NULL has no mount callback!\n", path, device); + return NULL; +} + +/// Filesystem information. +static file_system_type null_file_system_type = { + .name = "null", + .fs_flags = 0, + .mount = null_mount_callback +}; static vfs_file_operations_t null_fs_operations = { .open_f = null_open, @@ -72,10 +92,11 @@ static vfs_file_operations_t null_fs_operations = { .getdents_f = NULL }; -static struct memdev *null_device_create(const char* name) { +static struct memdev *null_device_create(const char *name) +{ // Create the device. struct memdev *dev = kmalloc(sizeof(struct memdev)); - dev->next = NULL; + dev->next = NULL; // Create the file. vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); @@ -87,13 +108,13 @@ static struct memdev *null_device_create(const char* name) { // Set the device name. strncpy(file->name, name, NAME_MAX); - file->count = 0; - file->uid = 0; - file->gid = 0; - file->mask = 0x2000 | 0666; - file->atime = sys_time(NULL); - file->mtime = sys_time(NULL); - file->ctime = sys_time(NULL); + file->count = 0; + file->uid = 0; + file->gid = 0; + file->mask = 0x2000 | 0666; + file->atime = sys_time(NULL); + file->mtime = sys_time(NULL); + file->ctime = sys_time(NULL); file->length = 0; // Set the operations. file->sys_operations = &mem_sys_operations; @@ -101,8 +122,9 @@ static struct memdev *null_device_create(const char* name) { return dev; } -static vfs_file_t* null_open(const char *path, int flags, mode_t mode) { - vfs_file_t* file = find_device_file(path); +static vfs_file_t *null_open(const char *path, int flags, mode_t mode) +{ + vfs_file_t *file = find_device_file(path); if (file) { file->count++; } @@ -110,21 +132,24 @@ static vfs_file_t* null_open(const char *path, int flags, mode_t mode) { return file; } -static int null_close(vfs_file_t * file) { +static int null_close(vfs_file_t *file) +{ assert(file && "Received null file."); file->count--; return 0; } -static ssize_t null_write(vfs_file_t * file, const void *buffer, off_t offset, size_t size) { +static ssize_t null_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size) +{ return size; } -static ssize_t null_read(vfs_file_t * file, char *buffer, off_t offset, size_t size) { +static ssize_t null_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) +{ return 0; } -static int null_fstat(vfs_file_t * file, stat_t *stat) +static int null_fstat(vfs_file_t *file, stat_t *stat) { pr_debug("null_fstat(%s, %p)\n", file->name, stat); stat->st_dev = 0; @@ -139,8 +164,9 @@ static int null_fstat(vfs_file_t * file, stat_t *stat) return 0; } -static int mem_stat(const char *path, stat_t *stat) { - vfs_file_t* file = find_device_file(path); +static int mem_stat(const char *path, stat_t *stat) +{ + vfs_file_t *file = find_device_file(path); if (file) { return file->fs_operations->stat_f(file, stat); @@ -155,13 +181,14 @@ int mem_devs_initialize(void) pr_err("Failed to create devnull"); return -ENODEV; } - - if (!vfs_mount("/dev/null", devnull->file)) { - pr_err("Failed to mount /dev/null"); + if (!vfs_register_filesystem(&null_file_system_type)) { + pr_err("Failed to register NULL filesystem."); + return 1; + } + if (!vfs_register_superblock("null", "/dev/null", &null_file_system_type, devnull->file)) { + pr_err("Failed to mount /dev/null."); return 1; } - add_device(devnull); - return 0; } diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index 320d26ba..2ec0f699 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -8,6 +8,8 @@ #define __DEBUG_HEADER__ "[EXT2 ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. +// If defined, ETX2 will debug everything. +// #define EXT2_FULL_DEBUG #include "assert.h" #include "fcntl.h" @@ -21,6 +23,7 @@ #include "stdio.h" #include "string.h" #include "sys/errno.h" +#include "sys/stat.h" #define EXT2_SUPERBLOCK_MAGIC 0xEF53 ///< Magic value used to identify an ext2 filesystem. #define EXT2_DIRECT_BLOCKS 12 ///< Amount of indirect blocks in an inode. @@ -28,16 +31,6 @@ #define EXT2_MAX_SYMLINK_COUNT 8 ///< Maximum nesting of symlinks, used to prevent a loop. #define EXT2_NAME_LEN 255 ///< The lenght of names inside directory entries. -// File types. -#define EXT2_S_IFMT 0xF000 ///< Format mask -#define EXT2_S_IFSOCK 0xC000 ///< Socket -#define EXT2_S_IFLNK 0xA000 ///< Symbolic link -#define EXT2_S_IFREG 0x8000 ///< Regular file -#define EXT2_S_IFBLK 0x6000 ///< Block device -#define EXT2_S_IFDIR 0x4000 ///< Directory -#define EXT2_S_IFCHR 0x2000 ///< Character device -#define EXT2_S_IFIFO 0x1000 ///< Fifo - // Permissions bit. #define EXT2_S_ISUID 0x0800 ///< SUID #define EXT2_S_ISGID 0x0400 ///< SGID @@ -377,7 +370,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 ssize_t ext2_readlink(const char *path, char *buffer, size_t bufsize); static int ext2_fsetattr(vfs_file_t *file, struct iattr *attr); static int ext2_mkdir(const char *path, mode_t mode); @@ -594,7 +587,7 @@ static void ext2_dump_inode(ext2_filesystem_t *fs, ext2_inode_t *inode) /// @param dirent the object to dump. static void ext2_dump_dirent(ext2_dirent_t *dirent) { - pr_debug("Inode: %4u Rec. Len.: %4u Name Len.: %4u Type:%4s Name: %s\n", dirent->inode, dirent->rec_len, dirent->name_len, ext2_file_type_to_string(dirent->file_type), dirent->name); + pr_debug("Inode: %4u Rec. Len.: %4u Name Len.: %4u Type: %4s Name: %s\n", dirent->inode, dirent->rec_len, dirent->name_len, ext2_file_type_to_string(dirent->file_type), dirent->name); } /// @brief Dumps on debugging output the BGDT. @@ -991,15 +984,15 @@ static int ext2_write_block(ext2_filesystem_t *fs, uint32_t block_index, uint8_t /// @return 0 on success, -1 on failure. static int ext2_read_bgdt(ext2_filesystem_t *fs) { - pr_debug("Read BGDT for EXT2 filesystem (0x%x)\n", fs); - if (fs->block_groups) { - for (uint32_t i = 0; i < fs->bgdt_length; ++i) { - ext2_read_block(fs, fs->bgdt_start_block + i, (uint8_t *)((uintptr_t)fs->block_groups + (fs->block_size * i))); - } - return 0; + pr_debug("ext2_read_bgdt(%p)\n", fs); + if (!fs->block_groups) { + pr_err("The `block_groups` list is not initialized.\n"); + return -1; } - pr_err("The `block_groups` list is not initialized.\n"); - return -1; + for (uint32_t i = 0; i < fs->bgdt_length; ++i) { + ext2_read_block(fs, fs->bgdt_start_block + i, (uint8_t *)((uintptr_t)fs->block_groups + (fs->block_size * i))); + } + return 0; } /// @brief Writes the Block Group Descriptor Table (BGDT) to the block device associated with this filesystem. @@ -1007,15 +1000,15 @@ static int ext2_read_bgdt(ext2_filesystem_t *fs) /// @return 0 on success, -1 on failure. static int ext2_write_bgdt(ext2_filesystem_t *fs) { - pr_debug("Write BGDT for EXT2 filesystem (0x%x)\n", fs); - if (fs->block_groups) { - for (uint32_t i = 0; i < fs->bgdt_length; ++i) { - ext2_write_block(fs, fs->bgdt_start_block + i, (uint8_t *)((uintptr_t)fs->block_groups + (fs->block_size * i))); - } - return 0; + pr_debug("ext2_write_bgdt(%p)\n", fs); + if (!fs->block_groups) { + pr_err("The `block_groups` list is not initialized.\n"); + return -1; } - pr_err("The `block_groups` list is not initialized.\n"); - return -1; + for (uint32_t i = 0; i < fs->bgdt_length; ++i) { + ext2_write_block(fs, fs->bgdt_start_block + i, (uint8_t *)((uintptr_t)fs->block_groups + (fs->block_size * i))); + } + return 0; } /// @brief Reads an inode. @@ -1038,7 +1031,7 @@ static int ext2_read_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t block_index = ext2_inode_index_to_block_index(fs, inode_index); // Log the address to the inode. - // pr_debug("Read inode (inode_index:%4u, group_index:%4u, group_offset:%4u, block_index:%4u)\n", + // pr_debug("Read inode (inode_index: %4u, group_index: %4u, group_offset: %4u, block_index: %4u)\n", // inode_index, group_index, group_offset, block_index); // Check for error. @@ -1079,7 +1072,7 @@ static int ext2_write_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t // Get the block offest. block_index = ext2_inode_index_to_block_index(fs, inode_index); - // pr_debug("Write inode (inode_index:%4u, group_index:%4u, group_offset:%4u, block_index:%4u)\n", + // pr_debug("Write inode (inode_index: %4u, group_index: %4u, group_offset: %4u, block_index: %4u)\n", // inode_index, group_index, group_offset, block_index); // Check for error. @@ -1131,7 +1124,7 @@ static int ext2_allocate_inode(ext2_filesystem_t *fs, unsigned preferred_group) // Compute the inode index. inode_index = (group_index * fs->superblock.inodes_per_group) + group_offset + 1U; // Log the allocation of the inode. - pr_debug("Allocate inode, inode_index:%u, group_index:%4u, group_offset:%4u\n", inode_index, group_index, group_offset); + pr_debug("ext2_allocate_inode(inode: %4u, group_index: %4u, group_offset: %4u\n", inode_index, group_index, group_offset); // Set the inode as occupied. ext2_bitmap_set(cache, group_offset); // Write back the inode bitmap. @@ -1180,7 +1173,7 @@ static uint32_t ext2_allocate_block(ext2_filesystem_t *fs) // Compute the block index. block_index = (group_index * fs->superblock.blocks_per_group) + group_offset; // Log the allocation of the inode. - pr_debug("Allocate block (block_index:%u, group_index:%4u, group_offset:%4u)\n", block_index, group_index, group_offset); + pr_debug("ext2_allocate_block(block: %4u, group_index: %4u, group_offset: %4u)\n", block_index, group_index, group_offset); // Set the block as occupied. ext2_bitmap_set(cache, group_offset); // Update the bitmap. @@ -1222,7 +1215,7 @@ static void ext2_free_block(ext2_filesystem_t *fs, uint32_t block_index) uint32_t block_bitmap = fs->block_groups[group_index].block_bitmap; // Log the allocation of the inode. - pr_debug("Free block (block_index:%u, group_index:%4u, group_offset:%4u)\n", block_index, group_index, group_offset); + pr_debug("ext2_free_block(block: %4u, group_index: %4u, group_offset: %4u)\n", block_index, group_index, group_offset); // Allocate the cache. uint8_t *cache = ext2_alloc_cache(fs); @@ -1268,7 +1261,7 @@ static int ext2_free_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t uint32_t block_number = (inode->size / fs->block_size) + ((inode->size % fs->block_size) != 0); // Log the allocation of the inode. - pr_debug("Free inode (group_index:%u, inode_index:%4u, group_offset:%4u)\n", group_index, inode_index, group_offset); + pr_debug("ext2_free_inode(group: %4u, inode_index: %4u, group_offset: %4u)\n", group_index, inode_index, group_offset); // Free its blocks. for (uint32_t block_index = 0; block_index < block_number; ++block_index) { @@ -1384,14 +1377,13 @@ static int ext2_set_real_block_index( // Result of the operation. int ret = 0; - // Allocate the cache. - uint8_t *cache = ext2_alloc_cache(fs); - // Are we setting a DIRECT block pointer. a = ((int)block_index) - EXT2_DIRECT_BLOCKS; if (a <= 0) { inode->data.blocks.dir_blocks[block_index] = real_index; } else { + // Allocate the cache. + uint8_t *cache = ext2_alloc_cache(fs); // Are we setting an INDIRECT block pointer. b = a - p; if (b <= 0) { @@ -1471,10 +1463,10 @@ static int ext2_set_real_block_index( } } } + early_exit: + // Free the cache. + ext2_dealloc_cache(cache); } -early_exit: - // Free the cache. - ext2_dealloc_cache(cache); return ret; } @@ -1492,14 +1484,13 @@ static uint32_t ext2_get_real_block_index(ext2_filesystem_t *fs, ext2_inode_t *i // The real index. uint32_t real_index = 0; - // Allocate the cache. - uint8_t *cache = ext2_alloc_cache(fs); - // Check if the index is among the DIRECT blocks. a = block_index - EXT2_DIRECT_BLOCKS; if (a < 0) { real_index = inode->data.blocks.dir_blocks[block_index]; } else { + // Allocate the cache. + uint8_t *cache = ext2_alloc_cache(fs); // Check if the index is among the INDIRECT blocks. b = a - p; if (b < 0) { @@ -1543,9 +1534,9 @@ static uint32_t ext2_get_real_block_index(ext2_filesystem_t *fs, ext2_inode_t *i } } } + // Free the cache. + ext2_dealloc_cache(cache); } - // Free the cache. - ext2_dealloc_cache(cache); return real_index; } @@ -1557,7 +1548,6 @@ static uint32_t ext2_get_real_block_index(ext2_filesystem_t *fs, ext2_inode_t *i /// @return 0 on success, -1 on failure. static int ext2_allocate_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t inode_index, uint32_t block_index) { - pr_debug("Allocating block `%d` for inode `%d`.\n", block_index, inode_index); // Allocate the block. int real_index = ext2_allocate_block(fs); if (real_index == -1) { @@ -1567,6 +1557,7 @@ static int ext2_allocate_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, if (ext2_set_real_block_index(fs, inode, inode_index, block_index, real_index) == -1) { return -1; } + pr_debug("ext2_allocate_inode_block(inode: %4u, block: %4u, real: %4u)\n", inode_index, block_index, real_index); // Compute the new blocks count. uint32_t blocks_count = (block_index + 1) * fs->blocks_per_block_count; if (inode->blocks_count < blocks_count) { @@ -1578,7 +1569,6 @@ static int ext2_allocate_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, if (ext2_write_inode(fs, inode, inode_index) == -1) { return -1; } - pr_debug("Succesfully allocated block `%d` for inode `%d`.\n", block_index, inode_index); return 0; } @@ -1599,7 +1589,9 @@ static ssize_t ext2_read_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, return -1; } // Log the address to the inode block. - // pr_debug("Read inode block (block_index:%4u, real_index:%4u)\n", block_index, real_index); +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_read_inode_block(block: %4u, real: %4u)\n", block_index, real_index); +#endif // Read the block. return ext2_read_block(fs, real_index, buffer); } @@ -1614,9 +1606,6 @@ static ssize_t ext2_read_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, static ssize_t ext2_write_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t inode_index, uint32_t block_index, uint8_t *buffer) { while (block_index >= (inode->blocks_count / fs->blocks_per_block_count)) { - pr_debug("Write inode block: %u >= %u (%u / %u)\n", - block_index, inode->blocks_count / fs->blocks_per_block_count, - inode->blocks_count, fs->blocks_per_block_count); if (ext2_allocate_inode_block(fs, inode, inode_index, (inode->blocks_count / fs->blocks_per_block_count)) < 0) { pr_crit("Failed to write allocate inode block\n"); } @@ -1627,7 +1616,9 @@ static ssize_t ext2_write_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode return -1; } // Log the address to the inode block. - // pr_debug("Write inode block (block_index:%4u, real_index:%4u, inode_index:%4u)\n", block_index, real_index, inode_index); +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_write_inode_block(block: %4u, inode: %4u, real: %4u)\n", block_index, inode_index, real_index); +#endif // Write the block. return ext2_write_block(fs, real_index, buffer); } @@ -1652,6 +1643,10 @@ static ssize_t ext2_read_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, // How much bytes to read for the end block. uint32_t end_size = end_offset - end_block * fs->block_size; +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_read_inode_data(inode: %4u, offset: %4u, nbyte: %4u)\n", inode_index, offset, nbyte); +#endif + // Allocate the cache. uint8_t *cache = ext2_alloc_cache(fs); @@ -1661,7 +1656,7 @@ static ssize_t ext2_read_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, // Read the real block. if (ext2_read_inode_block(fs, inode, block_index, cache) == -1) { // TODO: Understand why sometimes it fails. - pr_warning("Failed to read the inode block %u of inode %u\n", block_index, inode_index); + pr_warning("Failed to read the inode block %4u of inode %4u\n", block_index, inode_index); // ret = -1; // break; } @@ -1708,6 +1703,10 @@ static ssize_t ext2_write_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, // How much bytes to read for the end block. uint32_t end_size = end_offset - end_block * fs->block_size; +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_write_inode_data(inode: %4u, offset: %4u, nbyte: %4u)\n", inode_index, offset, nbyte); +#endif + // Allocate the cache. uint8_t *cache = ext2_alloc_cache(fs); @@ -1715,7 +1714,9 @@ static ssize_t ext2_write_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, for (uint32_t block_index = start_block; block_index <= end_block; ++block_index) { left = 0, right = fs->block_size; // Read the real block. Do not check for - ext2_read_inode_block(fs, inode, block_index, cache); + if (ext2_read_inode_block(fs, inode, block_index, cache) < 0) { + pr_warning("Failed to read the inode block %4u of inode %4u\n", block_index, inode_index); + } if (block_index == start_block) { left = start_off; } @@ -1850,8 +1851,9 @@ static inline int ext2_directory_is_empty(ext2_filesystem_t *fs, uint8_t *cache, /// @return 0 on success, 1 on failure. static int ext2_clean_inode_content(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t inode_index) { + pr_debug("ext2_clean_inode_content(%p, %p, %u)\n", fs, inode, inode_index); // Check the type of operation. - if ((inode->mode & EXT2_S_IFREG) != EXT2_S_IFREG) { + if ((inode->mode & S_IFREG) != S_IFREG) { pr_alert("Trying to clean the content of a non-regular file.\n"); return 1; } @@ -1862,10 +1864,13 @@ static int ext2_clean_inode_content(ext2_filesystem_t *fs, ext2_inode_t *inode, // int ret = 0; for (ssize_t offset = 0, to_write; offset < inode->size;) { + pr_debug("ext2_clean_inode_content(%p, %p, %u): offset = %6u of size = %u\n", fs, inode, inode_index, offset, inode->size); // We do not want to extend the size of the inode. to_write = max(min(offset + cache_size, inode->size), 0); + pr_debug("ext2_clean_inode_content(%p, %p, %u): to_write = %6u\n", fs, inode, inode_index, to_write); // Override the content. ssize_t written = ext2_write_inode_data(fs, inode, inode_index, offset, to_write, (char *)cache); + pr_debug("ext2_clean_inode_content(%p, %p, %u): written = %6u\n", fs, inode, inode_index, written); if (written < 0) { pr_err("Failed to clean content of inode %d\n", inode_index); ret = -1; @@ -2170,7 +2175,7 @@ static int ext2_allocate_direntry( return -1; } // Check that the parent is a directory. - if (!bitmask_check(parent_inode.mode, EXT2_S_IFDIR)) { + if (!bitmask_check(parent_inode.mode, S_IFDIR)) { pr_err("The parent inode is not a directory (ino: %d, mode: %d).\n", parent_inode_index, parent_inode.mode); return -1; } @@ -2306,14 +2311,14 @@ static int ext2_find_direntry(ext2_filesystem_t *fs, ino_t ino, const char *name return -1; } // Check that the parent is a directory. - if (!bitmask_check(inode.mode, EXT2_S_IFDIR)) { - pr_err("The parent inode is not a directory (ino: %d, mode: %d).\n", ino, inode.mode); + if (!bitmask_check(inode.mode, S_IFDIR)) { + pr_err("The parent inode is not a directory (ino: %d, mode: %d, name: %s).\n", ino, inode.mode, name); return -1; } // Check that we are allowed to reach through the directory if (!__valid_x_permission(scheduler_get_current_process(), &inode)) { - pr_err("The parent inode has no x permission (ino: %d, mode: %d).\n", ino, inode.mode); + pr_err("The parent inode has no x permission (ino: %d, mode: %d, name: %s).\n", ino, inode.mode, name); return -EPERM; } @@ -2341,8 +2346,9 @@ static int ext2_find_direntry(ext2_filesystem_t *fs, ino_t ino, const char *name // Copy the inode of the parent, even if we did not find the entry. search->parent_inode = ino; // Check if we have found the entry. - if (it.direntry == NULL) + if (it.direntry == NULL) { goto free_cache_return_error; + } // Copy the direntry. search->direntry.inode = it.direntry->inode; search->direntry.rec_len = it.direntry->rec_len; @@ -2368,7 +2374,7 @@ static int ext2_find_direntry(ext2_filesystem_t *fs, ino_t ino, const char *name /// @param path the path of the entry we are looking for, it can be a relative path. /// @param search the output variable where we save the entry information. /// @return 0 on success, -errno on failure. -static int ext2_resolve_path(vfs_file_t *directory, char *path, ext2_direntry_search_t *search) +static int ext2_resolve_path(vfs_file_t *directory, const char *path, ext2_direntry_search_t *search) { // Check the pointers. if (directory == NULL) { @@ -2393,17 +2399,16 @@ static int ext2_resolve_path(vfs_file_t *directory, char *path, ext2_direntry_se if (strcmp(path, "/") == 0) { return ext2_find_direntry(fs, directory->ino, path, search); } - ino_t ino = directory->ino; - char *saveptr, *token = strtok_r(path, "/", &saveptr); - while (token) { - if (!ext2_find_direntry(fs, ino, token, search)) { - ino = search->direntry.inode; - } else { + ino_t ino = directory->ino; + char token[NAME_MAX] = { 0 }; + size_t offset = 0; + while (tokenize(path, "/", &offset, token, NAME_MAX)) { + if (ext2_find_direntry(fs, ino, token, search)) { return -1; } - token = strtok_r(NULL, "/", &saveptr); + ino = search->direntry.inode; } - pr_debug("resolve_path(directory: %s, path: %s) -> (%s, %d)\n", + pr_debug("ext2_resolve_path(directory: %s, path: %s) -> (%s, %d)\n", directory->name, path, search->direntry.name, search->direntry.inode); return 0; } @@ -2473,22 +2478,22 @@ static int ext2_init_vfs_file( file->gid = inode->gid; // Set the VFS specific flags. file->flags = 0; - if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) { + if ((inode->mode & S_IFREG) == S_IFREG) { file->flags |= DT_REG; } - if ((inode->mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) { + if ((inode->mode & S_IFDIR) == S_IFDIR) { file->flags |= DT_DIR; } - if ((inode->mode & EXT2_S_IFBLK) == EXT2_S_IFBLK) { + if ((inode->mode & S_IFBLK) == S_IFBLK) { file->flags |= DT_BLK; } - if ((inode->mode & EXT2_S_IFCHR) == EXT2_S_IFCHR) { + if ((inode->mode & S_IFCHR) == S_IFCHR) { file->flags |= DT_CHR; } - if ((inode->mode & EXT2_S_IFIFO) == EXT2_S_IFIFO) { + if ((inode->mode & S_IFIFO) == S_IFIFO) { file->flags |= DT_FIFO; } - if ((inode->mode & EXT2_S_IFLNK) == EXT2_S_IFLNK) { + if ((inode->mode & S_IFLNK) == S_IFLNK) { file->flags |= DT_LNK; } // Set the inode. @@ -2630,7 +2635,7 @@ static int ext2_create_inode( /// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) static vfs_file_t *ext2_creat(const char *path, mode_t mode) { - pr_debug("creat(path: \"%s\", mode: %d)\n", path, mode); + pr_debug("ext2_creat(path: `%s`, mode: %d)\n", path, mode); // Get the name of the directory. char parent_path[PATH_MAX]; if (!dirname(path, parent_path, sizeof(parent_path))) { @@ -2682,7 +2687,7 @@ static vfs_file_t *ext2_creat(const char *path, mode_t mode) return file; } // Set the inode mode. - mode = EXT2_S_IFREG | (0xFFF & mode); + mode = S_IFREG | (0xFFF & mode); // Get the group index of the parent. uint32_t group_index = ext2_inode_index_to_group_index(fs, parent->ino); // Create and initialize the new inode. @@ -2731,31 +2736,27 @@ static vfs_file_t *ext2_creat(const char *path, mode_t mode) /// @return The file descriptor of the opened file, otherwise returns -1. static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) { - pr_debug("open(path: \"%s\", flags: %d, mode: %d)\n", path, flags, mode); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("Cannot get the absolute path for path `%s`.\n", path); - return NULL; - } + pr_debug("ext2_open(path: '%s', flags: %d, mode: %d)\n", path, flags, mode); // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("Failed to get the EXT2 filesystem for absolute path `%s`.\n", absolute_path); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Failed to get the EXT2 filesystem.\n", + path, flags, mode); return NULL; } // Prepare the structure for the search. ext2_direntry_search_t search; memset(&search, 0, sizeof(ext2_direntry_search_t)); // First check, if a file with the given name already exists. - if (!ext2_resolve_path(fs->root, absolute_path, &search)) { + if (!ext2_resolve_path(fs->root, path, &search)) { if (bitmask_check(flags, O_CREAT) && bitmask_check(flags, O_EXCL)) { - pr_err("A file or directory already exists at `%s` (O_CREAT | O_EXCL).\n", absolute_path); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): A file or directory already exists (O_CREAT | O_EXCL).\n", + path, flags, mode); return NULL; } if (bitmask_check(flags, O_DIRECTORY) && (search.direntry.file_type != ext2_file_type_directory)) { - pr_err("Directory entry `%s` is not a directory.\n", search.direntry.name); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Directory entry `%s` is not a directory.\n", + path, flags, mode, search.direntry.name); errno = ENOTDIR; return NULL; } @@ -2764,33 +2765,35 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) if (bitmask_check(flags, O_CREAT)) { return ext2_creat(path, mode); } - pr_err("The file does not exist `%s`.\n", absolute_path); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): The file does not exist.\n", + path, flags, mode); errno = ENOENT; return NULL; } - if (search.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)); // Get the inode associated with the directory entry. if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { - pr_err("Failed to read the inode of `%s`.\n", search.direntry.name); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Failed to read the inode.\n", + path, flags, mode); return NULL; } if (!vfs_valid_open_permissions(flags, inode.mode, inode.uid, inode.gid)) { - pr_err("Task does not have access permission.\n"); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Task does not have access permission.\n", + path, flags, mode); errno = EACCES; return NULL; } // Check if the file is a regular file, and the user wants to write and truncate. - if (bitmask_exact(inode.mode, EXT2_S_IFREG) && (bitmask_exact(flags, O_RDWR | O_TRUNC) || bitmask_exact(flags, O_RDONLY | O_TRUNC))) { + if (bitmask_exact(inode.mode, S_IFREG) && + (bitmask_exact(flags, O_RDWR | O_TRUNC) || bitmask_exact(flags, O_RDONLY | O_TRUNC))) { // Clean the content of the newly created file. if (ext2_clean_inode_content(fs, &inode, search.direntry.inode) < 0) { - pr_err("Failed to clean the content of the newly created inode.\n"); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Failed to clean the content of the newly created inode.\n", + path, flags, mode); return NULL; } } @@ -2800,11 +2803,13 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) // Allocate the memory for the file. file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); if (file == NULL) { - pr_err("Failed to allocate memory for the EXT2 file.\n"); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Failed to allocate memory for the EXT2 file.\n", + path, flags, mode); return NULL; } if (ext2_init_vfs_file(fs, file, &inode, search.direntry.inode, search.direntry.name, search.direntry.name_len) == -1) { - pr_err("Failed to properly set the VFS file.\n"); + pr_err("ext2_open(path: '%s', flags: %d, mode: %d): Failed to properly set the VFS file.\n", + path, flags, mode); return NULL; } // Add the vfs_file to the list of associated files. @@ -2818,38 +2823,32 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) /// @return 0 on success, -errno on failure. static int ext2_unlink(const char *path) { - pr_debug("unlink(%s)\n", path); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("Cannot get the absolute path for path `%s`.\n", path); - return -ENOENT; - } + pr_debug("ext2_unlink(%s)\n", path); + int ret = 0; // Get the name of the entry we want to unlink. - const char *name = basename(absolute_path); + const char *name = basename(path); if (name == NULL) { - pr_err("Cannot get the basename from the absolute path `%s`.\n", absolute_path); + pr_err("ext2_unlink(%s): Cannot get the basename.\n", path); return -ENOENT; } // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("Failed to get the EXT2 filesystem for absolute path `%s`.\n", absolute_path); + pr_err("ext2_unlink(%s): Failed to get the EXT2 filesystem.\n", path); return -ENOENT; } // Prepare the structure for the search. ext2_direntry_search_t search; memset(&search, 0, sizeof(ext2_direntry_search_t)); // Resolve the path to the directory entry. - if (ext2_resolve_path(fs->root, absolute_path, &search)) { - pr_err("Failed to resolve path `%s`.\n", absolute_path); + if (ext2_resolve_path(fs->root, path, &search)) { + pr_err("ext2_unlink(%s): Failed to resolve path.\n", path); return -ENOENT; } // Get the inode associated with the parent directory entry. ext2_inode_t parent_inode; if (ext2_read_inode(fs, &parent_inode, search.parent_inode) == -1) { - pr_err("stat(%s): Failed to read the inode of parent of `%s`.\n", path, search.direntry.name); + pr_err("ext2_unlink(%s): Failed to read the inode of parent (%d).\n", path, search.parent_inode); return -ENOENT; } // Allocate the cache. @@ -2857,14 +2856,17 @@ static int ext2_unlink(const char *path) // Read the block where the direntry resides. if (ext2_read_inode_block(fs, &parent_inode, search.block_index, cache) == -1) { - pr_err("Failed to read the parent inode block `%d`\n", search.block_index); - goto free_cache_return_error; + pr_err("ext2_unlink(%s): Failed to read the parent inode block (%d).\n", path, search.block_index); + ret = -1; + goto early_exit; } // Get a pointer to the direntry. ext2_dirent_t *actual_dirent = (ext2_dirent_t *)((uintptr_t)cache + search.block_offset); if (actual_dirent == NULL) { - pr_err("We found a NULL ext2_dirent_t\n"); - goto free_cache_return_error; + pr_err("ext2_unlink(%s): We found a NULL ext2_dirent_t.\n", path); + + ret = -1; + goto early_exit; } // Clear the directory entry. actual_dirent->inode = 0; @@ -2873,14 +2875,17 @@ static int ext2_unlink(const char *path) actual_dirent->file_type = ext2_file_type_unknown; // Write back the parent directory block. if (!ext2_write_inode_block(fs, &parent_inode, search.parent_inode, search.block_index, cache)) { - pr_err("Failed to write the inode block `%d`\n", search.block_index); - goto free_cache_return_error; + pr_err("ext2_unlink(%s): Failed to write the inode block (%d).\n", path, search.block_index); + ret = -1; + goto early_exit; } // Read the inode of the direntry we want to unlink. ext2_inode_t inode; if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { - pr_err("Failed to read the inode of `%s`.\n", search.direntry.name); - goto free_cache_return_error; + pr_err("ext2_unlink(%s): Failed to read the inode (%d: %s).\n", + path, search.direntry.inode, search.direntry.name); + ret = -1; + goto early_exit; } if (--inode.links_count == 0) { // Free the inode. @@ -2888,18 +2893,16 @@ static int ext2_unlink(const char *path) } else { // Update the inode. if (ext2_write_inode(fs, &inode, search.direntry.inode) == -1) { - pr_err("Failed to update the inode of `%s`.\n", search.direntry.name); - goto free_cache_return_error; + pr_err("ext2_unlink(%s): Failed to update the inode (%d: %s).\n", + path, search.direntry.inode, search.direntry.name); + ret = -1; + goto early_exit; } } - - // Free the cache. - ext2_dealloc_cache(cache); - return 0; -free_cache_return_error: +early_exit: // Free the cache. ext2_dealloc_cache(cache); - return -1; + return ret; } /// @brief Closes the given file. @@ -2917,7 +2920,7 @@ static int ext2_close(vfs_file_t *file) if (file == fs->root) { return -1; } - pr_debug("close(ino: %d, file: \"%s\")\n", file->ino, file->name); + 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. @@ -2933,7 +2936,9 @@ static int ext2_close(vfs_file_t *file) /// @return The number of red bytes. static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nbyte) { - //pr_debug("read(%s, %p, %d, %d)\n", file->name, buffer, offset, nbyte); +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_read(file: %s, offset: %4u, nbyte: %4u)\n", file->name, offset, nbyte); +#endif // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { @@ -2947,7 +2952,7 @@ static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nb return -1; } // Disallow reading directories using read - if ((inode.mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) { + if ((inode.mode & S_IFDIR) == S_IFDIR) { pr_err("Reading a directory `%s` is not allowed.\n", file->name); return -EISDIR; } @@ -2962,6 +2967,9 @@ static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nb /// @return The number of written bytes. static ssize_t ext2_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte) { +#ifdef EXT2_FULL_DEBUG + pr_debug("ext2_write(file: %s, offset: %4u, nbyte: %4u)\n", file->name, offset, nbyte); +#endif // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { @@ -2994,6 +3002,7 @@ static ssize_t ext2_write(vfs_file_t *file, const void *buffer, off_t offset, si /// indicate the error. static off_t ext2_lseek(vfs_file_t *file, off_t offset, int whence) { + pr_debug("ext2_lseek(file: %s, offset: %4u, whence: %4u)\n", file->name, offset, whence); // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { @@ -3061,6 +3070,7 @@ static int ext2_fstat(vfs_file_t *file, stat_t *stat) pr_err("We received a NULL stat pointer.\n"); return -EFAULT; } + pr_debug("ext2_fstat(file: %s)\n", file->name); // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { @@ -3081,6 +3091,41 @@ static int ext2_fstat(vfs_file_t *file, stat_t *stat) return __ext2_stat(&inode, stat); } +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path where the file resides. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int ext2_stat(const char *path, stat_t *stat) +{ + pr_debug("ext2_stat(path: %s)\n", path); + // Get the EXT2 filesystem. + ext2_filesystem_t *fs = get_ext2_filesystem(path); + if (fs == NULL) { + pr_err("ext2_stat(path: %s): Failed to get the EXT2 filesystem.\n", path); + return -ENOENT; + } + // Prepare the structure for the search. + ext2_direntry_search_t search; + memset(&search, 0, sizeof(ext2_direntry_search_t)); + // Resolve the path. + if (ext2_resolve_path(fs->root, path, &search)) { + pr_err("ext2_stat(path: %s): Failed to resolve path.\n", path); + return -ENOENT; + } + // Get the inode associated with the directory entry. + ext2_inode_t inode; + if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { + 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); +} + /// @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 on which we perform the operations. @@ -3102,7 +3147,7 @@ static int ext2_ioctl(vfs_file_t *file, int request, void *data) /// @return The number of written bytes in the buffer. static ssize_t ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) { - pr_debug("getdents(%s, %p, %d, %d)\n", file->name, dirp, doff, count); + pr_debug("ext2_getdents(file: %s, doff: %4u, count: %4u)\n", file->name, doff, count); // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { @@ -3155,26 +3200,38 @@ static ssize_t ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_ /// @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. -static ssize_t ext2_readlink(vfs_file_t *file, char *buffer, size_t bufsize) +static ssize_t ext2_readlink(const char *path, char *buffer, size_t bufsize) { - // Get the filesystem. - ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; + pr_debug("ext2_readlink(path: %s)\n", path); + // Get the EXT2 filesystem. + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("The file does not belong to an EXT2 filesystem `%s`.\n", file->name); + pr_err("ext2_readlink(path: %s): Failed to get the EXT2 filesystem.\n", path); + return -ENOENT; + } + // Prepare the structure for the search. + ext2_direntry_search_t search; + memset(&search, 0, sizeof(ext2_direntry_search_t)); + // Resolve the path. + if (ext2_resolve_path(fs->root, path, &search)) { + pr_err("ext2_readlink(path: %s): Failed to resolve path.\n", path); 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); + if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { + pr_err("ext2_readlink(path: %s): Failed to read the inode (%d).\n", path, search.direntry.inode); 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); + ssize_t ret = -ENOENT; + if (S_ISLNK(inode.mode)) { + // Get the length of the symlink. + ret = min(strlen(inode.data.symlink), bufsize); + // Copy the symlink information. + strncpy(buffer, inode.data.symlink, ret); + } // Return how much we read. - return nbytes; + return ret; } /// @brief Creates a new directory at the given path. @@ -3183,39 +3240,32 @@ static ssize_t ext2_readlink(vfs_file_t *file, char *buffer, size_t bufsize) /// @return Returns a negative value on failure. static int ext2_mkdir(const char *path, mode_t permission) { - pr_debug("mkdir(%s, %d)\n", path, permission); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("mkdir(%s): Cannot get the absolute path.\n", path); - return -ENOENT; - } + pr_debug("ext2_mkdir(path: %s, permission: %u)\n", path, permission); // Get the parent directory. char parent_path[PATH_MAX]; - if (!dirname(absolute_path, parent_path, sizeof(parent_path))) { + if (!dirname(path, parent_path, sizeof(parent_path))) { return -ENOENT; } // Check the parent path. - if (strcmp(parent_path, absolute_path) == 0) { - pr_err("mkdir(%s): Failed to properly get the parent directory (%s == %s).\n", path, parent_path, absolute_path); + if (strcmp(parent_path, path) == 0) { + pr_err("ext2_mkdir(path: %s): Failed to properly get the parent directory (%s).\n", path, parent_path); return -ENOENT; } // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("mkdir(%s): Failed to get the EXT2 filesystem for absolute path `%s`.\n", path, absolute_path); + pr_err("ext2_mkdir(path: %s): Failed to get the EXT2 filesystem.\n", path); return -ENOENT; } // Prepare the structure for the search. ext2_direntry_search_t search; // Search if the entry already exists. - if (!ext2_resolve_path(fs->root, absolute_path, &search)) { - pr_err("mkdir(%s): Directory already exists.\n", absolute_path); + if (!ext2_resolve_path(fs->root, path, &search)) { + pr_err("ext2_mkdir(path: %s): Directory already exists.\n", path); return -EEXIST; } // Set the inode mode. - uint32_t mode = EXT2_S_IFDIR; + uint32_t mode = S_IFDIR; mode |= 0xFFF & permission; // Get the group index of the parent. uint32_t group_index = ext2_inode_index_to_group_index(fs, search.parent_inode); @@ -3224,7 +3274,7 @@ static int ext2_mkdir(const char *path, mode_t permission) // Create and initialize the new inode. int inode_index = ext2_create_inode(fs, &inode, mode, group_index); if (inode_index == -1) { - pr_err("mkdir(%s): Failed to create a new inode (group index: %d).\n", path, group_index); + pr_err("ext2_mkdir(path: %s): Failed to create a new inode (group index: %d).\n", path, group_index); return -ENOENT; } // Increase the number of directories inside the group. @@ -3238,22 +3288,22 @@ static int ext2_mkdir(const char *path, mode_t permission) const char *directory_name = basename(path); // Create a directory entry for the directory. if (ext2_allocate_direntry(fs, search.parent_inode, inode_index, directory_name, ext2_file_type_directory) == -1) { - pr_err("mkdir(%s): Failed to allocate the new direntry `%s` for the inode.\n", path, directory_name); + pr_err("ext2_mkdir(path: %s): Failed to allocate the new direntry `%s` for the inode.\n", path, directory_name); return -ENOENT; } // Allocate a new block. if (!ext2_initialize_new_direntry_block(fs, inode_index, 0)) { - pr_err("mkdir(%s): Failed to allocate a new block for an inode.\n", path); + pr_err("ext2_mkdir(path: %s): Failed to allocate a new block for an inode.\n", path); return 0; } // Create a directory entry, inside the new directory, pointing to itself. if (ext2_allocate_direntry(fs, inode_index, inode_index, ".", ext2_file_type_directory) == -1) { - pr_err("mkdir(%s): Failed to allocate a new direntry for the inode.\n", path); + pr_err("ext2_mkdir(path: %s): Failed to allocate a new direntry for the inode.\n", path); return -ENOENT; } // Create a directory entry, inside the new directory, pointing to its parent. if (ext2_allocate_direntry(fs, inode_index, search.parent_inode, "..", ext2_file_type_directory) == -1) { - pr_err("mkdir(%s): Failed to allocate a new direntry for the inode.\n", path); + pr_err("ext2_mkdir(path: %s): Failed to allocate a new direntry for the inode.\n", path); return -ENOENT; } return 0; @@ -3264,92 +3314,43 @@ static int ext2_mkdir(const char *path, mode_t permission) /// @return Returns a negative value on failure. static int ext2_rmdir(const char *path) { - pr_debug("rmdir(%s)\n", path); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("rmdir(%s): Cannot get the absolute path.\n", path); - return -ENOENT; - } + pr_debug("ext2_rmdir(path: %s)\n", path); // Get the name of the entry we want to unlink. - const char *name = basename(absolute_path); + const char *name = basename(path); if (name == NULL) { - pr_err("rmdir(%s): Cannot get the basename from the absolute path `%s`.\n", path, absolute_path); + pr_err("ext2_rmdir(path: %s): Cannot get the basename`.\n", path); return -ENOENT; } // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("rmdir(%s): Failed to get the EXT2 filesystem for absolute path `%s`.\n", path, absolute_path); + pr_err("ext2_rmdir(path: %s): Failed to get the EXT2 filesystem.\n", path); return -ENOENT; } // Prepare the structure for the search. ext2_direntry_search_t search; memset(&search, 0, sizeof(ext2_direntry_search_t)); // Resolve the path to the directory entry. - if (ext2_resolve_path(fs->root, absolute_path, &search)) { - pr_err("rmdir(%s): Failed to resolve path `%s`.\n", path, absolute_path); + if (ext2_resolve_path(fs->root, path, &search)) { + pr_err("ext2_rmdir(path: %s): Failed to resolve path.\n", path); return -ENOENT; } // Get the inode associated with the parent directory entry. ext2_inode_t parent; if (ext2_read_inode(fs, &parent, search.parent_inode) == -1) { - pr_err("rmdir(%s): Failed to read the inode of parent of `%s`.\n", path, search.direntry.name); + pr_err("ext2_rmdir(path: %s): Failed to read the inode of parent of `%s`.\n", path, search.direntry.name); return -ENOENT; } // Read the inode of the direntry we want to unlink. ext2_inode_t inode; if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { - pr_err("rmdir(%s): Failed to read the inode of `%s`.\n", path, search.direntry.name); + pr_err("ext2_rmdir(path: %s): Failed to read the inode of `%s`.\n", path, search.direntry.name); return -ENOENT; } return ext2_destroy_direntry(fs, parent, inode, search.parent_inode, search.direntry.inode, search.block_index, search.block_offset); } -/// @brief Retrieves information concerning the file at the given position. -/// @param path The path where the file resides. -/// @param stat The structure where the information are stored. -/// @return 0 if success. -static int ext2_stat(const char *path, stat_t *stat) -{ - pr_debug("stat(%s, %p)\n", path, stat); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("stat(%s): Cannot get the absolute path.\n", path); - return -ENOENT; - } - // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); - if (fs == NULL) { - pr_err("stat(%s): Failed to get the EXT2 filesystem for absolute path `%s`.\n", path, absolute_path); - return -ENOENT; - } - // Prepare the structure for the search. - ext2_direntry_search_t search; - memset(&search, 0, sizeof(ext2_direntry_search_t)); - // Resolve the path. - if (ext2_resolve_path(fs->root, absolute_path, &search)) { - pr_err("stat(%s): Failed to resolve path `%s`.\n", path, absolute_path); - return -ENOENT; - } - // Get the inode associated with the directory entry. - ext2_inode_t inode; - if (ext2_read_inode(fs, &inode, search.direntry.inode) == -1) { - pr_err("stat(%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); -} - /// @brief Sets the attributes of an inode and saves it /// @param inode The inode to set the attributes /// @param attr The structure where the attributes are stored. @@ -3392,6 +3393,7 @@ static int __ext2_check_setattr_permission(uid_t file_owner) /// @return 0 if success. static int ext2_fsetattr(vfs_file_t *file, struct iattr *attr) { + pr_debug("ext2_fsetattr(file: %s)\n", file->name); if (!__ext2_check_setattr_permission(file->uid)) { return -EPERM; } @@ -3417,26 +3419,19 @@ static int ext2_fsetattr(vfs_file_t *file, struct iattr *attr) /// @return 0 if success. static int ext2_setattr(const char *path, struct iattr *attr) { - pr_debug("setattr(%s, %p)\n", path, attr); - // Get the absolute path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("setattr(%s): Cannot get the absolute path.\n", path); - return -ENOENT; - } + pr_debug("ext2_setattr(file: %s)\n", path); // Get the EXT2 filesystem. - ext2_filesystem_t *fs = get_ext2_filesystem(absolute_path); + ext2_filesystem_t *fs = get_ext2_filesystem(path); if (fs == NULL) { - pr_err("setattr(%s): Failed to get the EXT2 filesystem for absolute path `%s`.\n", path, absolute_path); + pr_err("setattr(%s): Failed to get the EXT2 filesystem for absolute path `%s`.\n", path); return -ENOENT; } // Prepare the structure for the search. ext2_direntry_search_t search; memset(&search, 0, sizeof(ext2_direntry_search_t)); // Resolve the path. - if (ext2_resolve_path(fs->root, absolute_path, &search)) { - pr_err("setattr(%s): Failed to resolve path `%s`.\n", path, absolute_path); + if (ext2_resolve_path(fs->root, path, &search)) { + pr_err("setattr(%s): Failed to resolve path.\n", path); return -ENOENT; } // Get the inode associated with the directory entry. @@ -3458,6 +3453,7 @@ static int ext2_setattr(const char *path, struct iattr *attr) /// @return the VFS root node of the EXT2 filesystem. static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path) { + pr_debug("ext2_mount(device: %s, path: %s)\n", block_device->name, path); // Create the ext2 filesystem. ext2_filesystem_t *fs = kmalloc(sizeof(ext2_filesystem_t)); // Clean the memory. @@ -3552,7 +3548,7 @@ static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path) // Free the block_buffer, the block_groups and the filesystem. goto free_block_buffer; } - if ((root_inode.mode & EXT2_S_IFDIR) != EXT2_S_IFDIR) { + if ((root_inode.mode & S_IFDIR) != S_IFDIR) { pr_err("The root is not a directory.\n"); // Free the block_buffer, the block_groups and the filesystem. goto free_block_buffer; @@ -3607,16 +3603,9 @@ static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path) /// @return the VFS file of the filesystem. static vfs_file_t *ext2_mount_callback(const char *path, const char *device) { - // Allocate a variable for the path. - char absolute_path[PATH_MAX]; - // If the first character is not the '/' then get the absolute path. - if (!realpath(device, absolute_path, sizeof(absolute_path))) { - pr_err("mount_callback(%s, %s): Cannot get the absolute path.", path, device); - return NULL; - } - super_block_t *sb = vfs_get_superblock(absolute_path); + super_block_t *sb = vfs_get_superblock(device); if (sb == NULL) { - pr_err("mount_callback(%s, %s): Cannot find the superblock at absolute path `%s`!\n", path, device, absolute_path); + pr_err("mount_callback(%s, %s): Cannot find the superblock!\n", path, device); return NULL; } vfs_file_t *block_device = sb->root; diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c index 21b845e9..0d4be61d 100644 --- a/mentos/src/fs/namei.c +++ b/mentos/src/fs/namei.c @@ -3,33 +3,34 @@ /// @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__ "[NAMEI ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. + #include "fs/namei.h" #include "assert.h" #include "limits.h" #include "fcntl.h" +#include "sys/stat.h" #include "fs/vfs.h" -#include "io/debug.h" #include "process/scheduler.h" #include "sys/errno.h" +#include "strerror.h" #include "string.h" /// Appends the path with a "/" as separator. -#define APPEND_PATH_SEP_OR_FAIL(b, remaining) \ -{ \ - strncat(b, "/", remaining); \ - remaining--; \ - if (remaining < 0) \ - return -ENAMETOOLONG; \ -} +#define APPEND_PATH_SEPARATOR(buffer, buflen) \ + { \ + if (buffer[strnlen(buffer, buflen) - 1] != '/') { strncat(buffer, "/", buflen); } \ + } -/// Appends the path with a "/" as separator. -#define APPEND_PATH_OR_FAIL(b, path, remaining) \ -{ \ - strncat(b, path, remaining); \ - remaining -= strlen(path); \ - if (remaining < 0) \ - return -ENAMETOOLONG; \ -} +/// Appends the path. +#define APPEND_PATH(buffer, token) \ + { \ + strncat(buffer, token, strlen(token)); \ + } int sys_unlink(const char *path) { @@ -64,7 +65,7 @@ int sys_creat(const char *path, mode_t mode) // Set the file descriptor id. task->fd_list[fd].file_struct = file; - task->fd_list[fd].flags_mask = O_WRONLY|O_CREAT|O_TRUNC; + task->fd_list[fd].flags_mask = O_WRONLY | O_CREAT | O_TRUNC; // Return the file descriptor and increment it. return fd; @@ -77,20 +78,22 @@ int sys_symlink(const char *linkname, const char *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; + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // Resolve the path. + int ret = resolve_path(path, absolute_path, sizeof(absolute_path), 0); + if (ret < 0) { + pr_err("sys_readlink(%s): Cannot resolve path!\n", path); + return ret; } // Read the link. - ssize_t nbytes = vfs_readlink(file, buffer, bufsize); - // Close the file. - vfs_close(file); + ssize_t nbytes = vfs_readlink(path, buffer, bufsize); // Return the number of bytes we read. return nbytes; } -char *realpath(const char *path, char *buffer, size_t buflen) { +char *realpath(const char *path, char *buffer, size_t buflen) +{ int ret = resolve_path(path, buffer, buflen, REMOVE_TRAILING_SLASH); if (ret < 0) { errno = -ret; @@ -99,161 +102,133 @@ char *realpath(const char *path, char *buffer, size_t buflen) { return buffer; } -int resolve_path(const char *path, char *buffer, size_t buflen, int flags) +/// @brief Determines if the path points to a link. +/// @param path the path to the file. +/// @return 1 if it is a link, 0 otherwise. +static inline int __is_a_link(const char *path) { - assert(path && "Provided null path."); - assert(buffer && "Provided null buffer."); + stat_t statbuf; + if (vfs_stat(path, &statbuf) > 0) { + return S_ISLNK(statbuf.st_mode); + } + return 0; +} - // Buffer used to build up the absolute path - char abspath[buflen]; - // Null-terminate our work buffer - memset(abspath,0, buflen); - int remaining = buflen - 1; +/// @brief Returns the content of the link. +/// @param path the path to the file. +/// @param buffer the buffer where we store the link content. +/// @param buflen length of the buffer. +/// @return the length of the link, or a negative value on error. +static inline int __get_link_content(const char *path, char *buffer, size_t buflen) +{ + ssize_t link_length = vfs_readlink(path, buffer, buflen); + if (link_length < 0) { + return link_length; + } + // Null-terminate link. + buffer[link_length] = 0; + return link_length; +} - // Track the resolved symlinks to ensure we do not end up in a loop - int symlinks = 0; +/// @brief Resolve the path by following all symbolic links. +/// @param path the path to resolve. +/// @param buffer the buffer where the resolved path is stored. +/// @param buflen the size of the provided resolved_path buffer. +/// @param flags the flags controlling how the path is resolved. +/// @param link_depth the current link depth. +/// @return -errno on fail, 1 on success. +int __resolve_path(const char *path, char *abspath, size_t buflen, int flags, int link_depth) +{ + char token[NAME_MAX] = { 0 }; + char buffer[PATH_MAX] = { 0 }; + char linkpath[PATH_MAX] = { 0 }; + size_t offset = 0, linklen = 0, tokenlen = 0; + int contains_links = 0; + stat_t statbuf; if (path[0] != '/') { // Get the working directory of the current task. - sys_getcwd(abspath, remaining); - // Check the absolute path. - assert((strlen(abspath) > 0) && "Current working directory is not set."); - // Check that the current working directory is an absolute path. - assert((abspath[0] == '/') && "Current working directory is not an absolute path."); - // Count the remaining space in the absolute path. - remaining -= strlen(abspath); - APPEND_PATH_SEP_OR_FAIL(abspath, remaining); + sys_getcwd(buffer, buflen); + pr_debug("|%-32s|%-32s| (INIT)\n", path, buffer); + } else { + pr_debug("|%-32s|%-32s| (INIT)\n", path, buffer); } - // Copy the path into the working buffer; - APPEND_PATH_OR_FAIL(abspath, path, remaining); - - int absidx, pathidx; -resolve_abspath: - absidx = pathidx = 0; - while (abspath[absidx]) { - // Skip multiple consecutive / characters - if (!strncmp("//", abspath + absidx, 2)) { - absidx++; - } - // Go to previous directory if /../ is found - else if (!strncmp("/../", abspath + absidx, 4)) { - // Go to a valid path character (pathidx points to the next one) - if (pathidx) { - pathidx--; - } - while (pathidx && buffer[pathidx] != '/') { - pathidx--; + while (tokenize(path, "/", &offset, token, NAME_MAX)) { + tokenlen = strlen(token); + if ((strcmp(token, "..") == 0) && (tokenlen == 2)) { + // Handle parent directory token "..". + if (strlen(buffer) > 0) { + // Find the last occurrence of '/'. + char *last_slash = strrchr(buffer, '/'); + if (!last_slash || (last_slash == buffer)) { + // This case handles if buffer is already empty (e.g., ".." + // from the root). + buffer[0] = '/'; + buffer[1] = 0; + pr_debug("|%-32s|%-32s| (RESET)\n", path, buffer); + } else { + // Truncate at the last slash. + *last_slash = '\0'; + pr_debug("|%-32s|%-32s| (TRUNCATE)\n", path, buffer); + } } - absidx += 3; - } else if (!strncmp("/./", abspath + absidx, 3)) { - absidx += 2; + } else if ((strcmp(token, ".") == 0) && (tokenlen == 1)) { + // Nothing to do. } else { - // Resolve a possible symlink - if (flags & FOLLOW_LINKS) { - // Write the next path separator - buffer[pathidx++] = abspath[absidx++]; - - // Find the next separator after the current path component - char* sep_after_cur = strchr(abspath + absidx, '/'); - if (sep_after_cur) { - // Null-terminate work buffer to properly open the current component - *sep_after_cur = 0; - } - - stat_t statbuf; - int ret = vfs_stat(abspath, &statbuf); - if (ret < 0) { - // This is the last path component and we want to create it anyway. - if (ret == -ENOENT && !sep_after_cur && (flags & CREAT_LAST_COMPONENT)) { - // Just copy the component into the buffer - goto copy_path_component; - } - return ret; - } - - // The path component is no symbolic link - if (!S_ISLNK(statbuf.st_mode)) { - // Restore replaced path separator - *sep_after_cur = '/'; - // Just copy the component into the buffer - goto copy_path_component; - } - - symlinks++; - if (symlinks > SYMLOOP_MAX) { return -ELOOP; } - - char link[PATH_MAX]; - vfs_file_t* link_file = vfs_open_abspath(abspath, O_RDONLY, 0); - if (link_file == NULL) { return -errno; } - ssize_t nbytes = vfs_readlink(link_file, link, sizeof(link)); - vfs_close(link_file); - if (nbytes == -1) { return -errno; } - - // Null-terminate link - link[nbytes] = 0; - - // Link is an absolute path, replace everything and continue - if (link[0] == '/') { - // Save the rest of the current abspath into the buffer - if (sep_after_cur) { - strncpy(buffer, sep_after_cur + 1, buflen); + if (strlen(buffer) + tokenlen + 1 < buflen) { + APPEND_PATH_SEPARATOR(buffer, buflen); + APPEND_PATH(buffer, token); + pr_debug("|%-32s|%-32s|%d| (APPEND)\n", path, buffer, tokenlen); + } else { + pr_err("Buffer overflow while resolving path.\n"); + return -ENAMETOOLONG; + } + if ((flags & FOLLOW_LINKS) && __is_a_link(buffer)) { + ssize_t link_length = __get_link_content(buffer, linkpath, PATH_MAX); + if (link_length > 0) { + if (link_depth >= SYMLOOP_MAX) { + pr_err("Reached symbolic link maximum depth `%d`.\n", link_depth); + return -ELOOP; } - - // Reset abspath with link - remaining = buflen - 1; - strncpy(abspath, link, remaining); - - remaining -= strlen(link); - if (remaining < 0) { return -ENAMETOOLONG; } - - // This is not the last component, therefore it must be a directory - if (sep_after_cur) { - APPEND_PATH_SEP_OR_FAIL(abspath, remaining); - // Copy the saved content from buffer back to the abspath - APPEND_PATH_OR_FAIL(abspath, buffer, remaining); + linklen = strlen(linkpath); + + if (linkpath[0] == '/') { + memcpy(buffer, linkpath, linklen); + pr_debug("|%-32s|%-32s| (REPLACE)\n", path, buffer); + } else { + // Find the last occurrence of '/'. + char *last_slash = strrchr(buffer, '/'); + if (last_slash) { + memcpy(++last_slash, linkpath, linklen); + pr_debug("|%-32s|%-32s|%-32s| (LINK)\n", path, buffer, linkpath); + } } - goto resolve_abspath; - // Link is relative, add it and continue - } else { - // Recalculate the remaining space in the working buffer - remaining = buflen - absidx - 1; - strncpy(abspath + absidx, link, remaining); - - remaining -= strlen(link); - if (remaining < 0) { return -ENAMETOOLONG; } - - if (sep_after_cur) { - // Restore replaced path separator - *sep_after_cur = '/'; - } - - // Continue with to previous path separator - absidx--; - pathidx--; + contains_links = 1; } - - } - // Copy the path component - else { -copy_path_component: - do { - buffer[pathidx++] = abspath[absidx++]; - } - while (abspath[absidx] && abspath[absidx] != '/'); } } } - - // Null-terminate buffer - buffer[pathidx] = 0; - - if (flags & REMOVE_TRAILING_SLASH) { - // Ensure the path is not just "/" - if (pathidx > 1 && buffer[pathidx] == '/') { - buffer[pathidx - 1] = '\0'; - } + if (contains_links) { + pr_debug("|%-32s|%-32s| (RECU)\n", path, buffer); + return __resolve_path(buffer, abspath, buflen, flags, ++link_depth); } - + // Get the end of the buffer. + size_t buffer_end = strnlen(buffer, buflen); + // If the buffer is empty, set it to '/', or remove trailing slash if requested. + if (buffer_end == 0) { + buffer[0] = '/'; + buffer[1] = 0; + } else if ((flags & REMOVE_TRAILING_SLASH) && (buffer_end > 1) && (buffer[buffer_end - 1] == '/')) { + pr_debug("|%-32s|%-32s|(%u) (REMTRAIL)\n", path, buffer, buffer_end); + buffer[buffer_end] = 0; + } + strncpy(abspath, buffer, buflen); + pr_debug("|%-32s|%-32s|(%u) (END)\n", path, buffer, buffer_end); return 0; } + +int resolve_path(const char *path, char *abspath, size_t buflen, int flags) +{ + return __resolve_path(path, abspath, buflen, flags, 0); +} diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c index f5db354d..a7e28b01 100644 --- a/mentos/src/fs/procfs.c +++ b/mentos/src/fs/procfs.c @@ -11,6 +11,7 @@ #include "assert.h" #include "fcntl.h" +#include "sys/stat.h" #include "fs/procfs.h" #include "fs/vfs.h" #include "libgen.h" @@ -878,7 +879,7 @@ int procfs_cleanup_module(void) // Destroy the cache. kmem_cache_destroy(fs.procfs_file_cache); // Unregister the filesystem. - vfs_register_filesystem(&procfs_file_system_type); + vfs_unregister_filesystem(&procfs_file_system_type); return 0; } diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 4620bccc..2bfaf033 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -10,6 +10,7 @@ #include "io/debug.h" // Include debugging functions. #include "fcntl.h" +#include "sys/stat.h" #include "assert.h" #include "fs/procfs.h" #include "fs/namei.h" @@ -61,11 +62,11 @@ void vfs_init(void) int vfs_register_filesystem(file_system_type *fs) { - if (hashmap_set(vfs_filesystems, (void *)fs->name, (void *)fs) != NULL) { + if (hashmap_set(vfs_filesystems, fs->name, fs) != NULL) { pr_err("Filesystem already registered.\n"); return 0; } - pr_debug("vfs_register_filesystem(`%s`) : %p\n", fs->name, fs); + pr_debug("vfs_register_filesystem(name: %s)\n", fs->name); return 1; } @@ -75,35 +76,66 @@ int vfs_unregister_filesystem(file_system_type *fs) pr_err("Filesystem not present to unregister.\n"); return 0; } + pr_debug("vfs_unregister_filesystem(name: %s)\n", fs->name); return 1; } -super_block_t *vfs_get_superblock(const char *absolute_path) +int vfs_register_superblock(const char *name, const char *path, file_system_type *type, vfs_file_t *root) { - size_t last_sb_len = 0; - super_block_t *last_sb = NULL, *superblock = NULL; + pr_debug("vfs_register_superblock(name: %s, path: %s, type: %s)\n", name, path, type->name); + // 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. + 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"); + + // Unlock the vfs spinlock. + spinlock_unlock(&vfs_spinlock); + return 1; +} + +int vfs_unregister_superblock(super_block_t *sb) +{ + pr_debug("vfs_unregister_superblock(name: %s, path: %s, type: %s)\n", sb->name, sb->path, sb->type->name); + list_head_remove(&sb->mounts); + kmem_cache_free(sb); + return 1; +} + +super_block_t *vfs_get_superblock(const char *path) +{ + pr_debug("vfs_get_superblock(path: %s)\n", path); + size_t last_sb_len = 0, len; + super_block_t *last_sb = NULL, *sb = NULL; list_head *it; list_for_each (it, &vfs_super_blocks) { - superblock = list_entry(it, super_block_t, mounts); -#if 0 - int len = strlen(superblock->name); - pr_debug("`%s` vs `%s`\n", absolute_path, superblock->name); - if (!strncmp(absolute_path, superblock->name, len)) { - size_t sbl = strlen(superblock->name); - if (sbl > last_sb_len) { - last_sb_len = sbl; - last_sb = superblock; - } - } -#else - size_t len = strlen(superblock->path); - if (!strncmp(absolute_path, superblock->path, len)) { + sb = list_entry(it, super_block_t, mounts); + len = strlen(sb->path); + if (!strncmp(path, sb->path, len)) { if (len > last_sb_len) { last_sb_len = len; - last_sb = superblock; + last_sb = sb; } } -#endif } return last_sb; } @@ -112,13 +144,13 @@ vfs_file_t *vfs_open_abspath(const char *absolute_path, int flags, mode_t mode) { super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { - pr_err("vfs_open(%s): Cannot find the superblock!\n", absolute_path); + pr_err("vfs_open_abspath(%s): Cannot find the superblock!\n", absolute_path); errno = ENOENT; return NULL; } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_open(%s): Cannot find the superblock root!\n", absolute_path); + pr_err("vfs_open_abspath(%s): Cannot find the superblock root!\n", absolute_path); errno = ENOENT; return NULL; } @@ -126,14 +158,14 @@ vfs_file_t *vfs_open_abspath(const char *absolute_path, int flags, mode_t mode) //size_t name_offset = (strcmp(mp->name, "/") == 0) ? 0 : strlen(mp->name); // Check if the function is implemented. if (sb_root->fs_operations->open_f == NULL) { - pr_err("vfs_open(%s): Function not supported in current filesystem.", absolute_path); + pr_err("vfs_open_abspath(%s): Function not supported in current filesystem.\n", absolute_path); errno = ENOSYS; return NULL; } // Retrieve the file. vfs_file_t *file = sb_root->fs_operations->open_f(absolute_path, flags, mode); if (file == NULL) { - pr_debug("vfs_open(%s): Filesystem open returned NULL file (errno: %d, %s)!\n", + pr_debug("vfs_open_abspath(%s): Filesystem open returned NULL file (errno: %d, %s)!\n", absolute_path, errno, strerror(errno)); return NULL; } @@ -147,20 +179,22 @@ vfs_file_t *vfs_open(const char *path, int flags, mode_t mode) { pr_debug("vfs_open(path: %s, flags: %d, mode: %d)\n", path, flags, mode); assert(path && "Provided null path."); - // Allocate a variable for the path. - char absolute_path[PATH_MAX]; // Resolve all symbolic links in the path before opening the file. - int resolve_flags = FOLLOW_LINKS; + int resolve_flags = FOLLOW_LINKS | REMOVE_TRAILING_SLASH; // Allow the last component to be non existing when attempting to create it. if (bitmask_check(flags, O_CREAT)) { resolve_flags |= CREAT_LAST_COMPONENT; } - int ret = resolve_path(path, absolute_path, sizeof(absolute_path), resolve_flags); + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); if (ret < 0) { pr_err("vfs_open(%s): Cannot resolve path!\n", path); errno = -ret; return NULL; } + pr_debug("vfs_open(path: %s, flags: %d, mode: %d) -> %s\n", path, flags, mode, absolute_path); return vfs_open_abspath(absolute_path, flags, mode); } @@ -229,9 +263,11 @@ int vfs_unlink(const char *path) // Allocate a variable for the path. char absolute_path[PATH_MAX]; // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("vfs_unlink(%s): Cannot get the absolute path.", path); - return -ENODEV; + int resolve_flags = REMOVE_TRAILING_SLASH | FOLLOW_LINKS; + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { + pr_err("vfs_unlink(%s): Cannot get the absolute path.\n", path); + return ret; } super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { @@ -240,12 +276,12 @@ int vfs_unlink(const char *path) } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_unlink(%s): Cannot find the superblock root.", path); + pr_err("vfs_unlink(%s): Cannot find the superblock root.\n", path); return -ENOENT; } // Check if the function is implemented. if (sb_root->fs_operations->unlink_f == NULL) { - pr_err("vfs_unlink(%s): Function not supported in current filesystem.", path); + pr_err("vfs_unlink(%s): Function not supported in current filesystem.\n", path); return -ENOSYS; } return sb_root->fs_operations->unlink_f(absolute_path); @@ -253,13 +289,17 @@ int vfs_unlink(const char *path) int vfs_mkdir(const char *path, mode_t mode) { + pr_debug("vfs_mkdir(path: %s, mode: %d)\n", path, mode); // Allocate a variable for the path. char absolute_path[PATH_MAX]; // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("vfs_mkdir(%s): Cannot get the absolute path.", path); - return -ENODEV; + int resolve_flags = REMOVE_TRAILING_SLASH | FOLLOW_LINKS | CREAT_LAST_COMPONENT; + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { + pr_err("vfs_mkdir(%s): Cannot get the absolute path.\n", path); + return ret; } + pr_debug("vfs_mkdir(path: %s, mode: %d) -> absolute_path: %s\n", path, mode, absolute_path); super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { pr_err("vfs_mkdir(%s): Cannot find the superblock!\n"); @@ -267,12 +307,12 @@ int vfs_mkdir(const char *path, mode_t mode) } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_mkdir(%s): Cannot find the superblock root.", path); + pr_err("vfs_mkdir(%s): Cannot find the superblock root.\n", path); return -ENOENT; } // Check if the function is implemented. if (sb_root->sys_operations->mkdir_f == NULL) { - pr_err("vfs_mkdir(%s): Function not supported in current filesystem.", path); + pr_err("vfs_mkdir(%s): Function not supported in current filesystem.\n", path); return -ENOSYS; } return sb_root->sys_operations->mkdir_f(absolute_path, mode); @@ -283,9 +323,11 @@ int vfs_rmdir(const char *path) // Allocate a variable for the path. char absolute_path[PATH_MAX]; // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("vfs_rmdir(%s): Cannot get the absolute path.", path); - return -ENODEV; + int resolve_flags = REMOVE_TRAILING_SLASH | FOLLOW_LINKS; + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { + pr_err("vfs_rmdir(%s): Cannot get the absolute path.\n", path); + return ret; } super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { @@ -294,12 +336,12 @@ int vfs_rmdir(const char *path) } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_rmdir(%s): Cannot find the superblock root.", path); + pr_err("vfs_rmdir(%s): Cannot find the superblock root.\n", path); return -ENOENT; } // Check if the function is implemented. if (sb_root->sys_operations->rmdir_f == NULL) { - pr_err("vfs_rmdir(%s): Function not supported in current filesystem.", path); + pr_err("vfs_rmdir(%s): Function not supported in current filesystem.\n", path); return -ENOSYS; } // Remove the file. @@ -311,9 +353,11 @@ vfs_file_t *vfs_creat(const char *path, mode_t mode) // Allocate a variable for the path. char absolute_path[PATH_MAX]; // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(absolute_path))) { - pr_err("vfs_creat(%s): Cannot get the absolute path.", path); - errno = ENODEV; + int resolve_flags = REMOVE_TRAILING_SLASH | FOLLOW_LINKS; + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { + pr_err("vfs_creat(%s): Cannot get the absolute path.\n", path); + errno = ret; return NULL; } super_block_t *sb = vfs_get_superblock(absolute_path); @@ -324,13 +368,13 @@ vfs_file_t *vfs_creat(const char *path, mode_t mode) } vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("vfs_creat(%s): Cannot find the superblock root.", path); + pr_err("vfs_creat(%s): Cannot find the superblock root.\n", path); errno = ENOENT; return NULL; } // Check if the function is implemented. if (sb_root->sys_operations->creat_f == NULL) { - pr_err("vfs_creat(%s): Function not supported in current filesystem.", path); + pr_err("vfs_creat(%s): Function not supported in current filesystem.\n", path); errno = ENOSYS; return NULL; } @@ -347,18 +391,31 @@ 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) +ssize_t vfs_readlink(const char *path, char *buffer, size_t bufsize) { - if (file == NULL) { - pr_err("vfs_readlink: received a null pointer for file.\n"); + pr_debug("vfs_readlink(%s, %s, %d)\n", path, buffer, bufsize); + // Allocate a variable for the path. + char absolute_path[PATH_MAX] = { 0 }; + // 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_readlink(%s, %s, %d): Cannot get the absolute path.", path, buffer, bufsize); + return ret; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_readlink(%s, %s, %d): Cannot find the superblock!.\n", path, buffer, bufsize); return -ENOENT; } - if (file->fs_operations->readlink_f == NULL) { - pr_err("vfs_readlink(%s): Function not supported in current filesystem.", file->name); - return -ENOSYS; + if (sb->root == NULL) { + pr_err("vfs_readlink(%s, %s, %d): Cannot find the superblock root.\n", path, buffer, bufsize); + return -ENOENT; + } + if (sb->root->fs_operations->readlink_f == NULL) { + return -ENOENT; } // Perform the read. - return file->fs_operations->readlink_f(file, buffer, bufsize); + return sb->root->fs_operations->readlink_f(absolute_path, buffer, bufsize); } int vfs_symlink(const char *linkname, const char *path) @@ -366,9 +423,11 @@ int vfs_symlink(const char *linkname, const char *path) // Allocate a variable for the path. char absolute_path[PATH_MAX]; // If the first character is not the '/' then get the absolute path. - if (!realpath(linkname, absolute_path, sizeof(absolute_path))) { + int resolve_flags = REMOVE_TRAILING_SLASH | FOLLOW_LINKS; + int ret = resolve_path(path, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { pr_err("vfs_symlink(%s, %s): Cannot get the absolute path.", linkname, path); - return -ENODEV; + return ret; } super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { @@ -391,12 +450,14 @@ int vfs_symlink(const char *linkname, const char *path) int vfs_stat(const char *path, stat_t *buf) { + pr_debug("vfs_stat(path: %s, buf: %p)\n", path, buf); // Allocate a variable for the path. - char absolute_path[PATH_MAX]; + char absolute_path[PATH_MAX] = { 0 }; // If the first character is not the '/' then get the absolute path. - if (!realpath(path, absolute_path, sizeof(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); - return -ENODEV; + return ret; } super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { @@ -414,15 +475,7 @@ int vfs_stat(const char *path, stat_t *buf) return -ENOSYS; } // Reset the structure. - buf->st_dev = 0; - buf->st_ino = 0; - buf->st_mode = 0; - buf->st_uid = 0; - buf->st_gid = 0; - buf->st_size = 0; - buf->st_atime = 0; - buf->st_mtime = 0; - buf->st_ctime = 0; + memset(buf, 0, sizeof(stat_t)); // Retrieve the file. return sb_root->sys_operations->stat_f(absolute_path, buf); } @@ -446,39 +499,7 @@ int vfs_fstat(vfs_file_t *file, stat_t *buf) return file->fs_operations->stat_f(file, buf); } -int vfs_mount(const char *path, vfs_file_t *new_fs_root) -{ - if (!path || path[0] != '/') { - pr_err("vfs_mount(%s): Path must be absolute for superblock.\n", path); - return 0; - } - if (new_fs_root == NULL) { - pr_err("vfs_mount(%s): You must provide a valid file!\n", path); - return 0; - } - // Lock the vfs spinlock. - spinlock_lock(&vfs_spinlock); - pr_debug("Mounting file with path `%s` as root '%s'...\n", new_fs_root->name, path); - // Create the superblock. - super_block_t *sb = kmem_cache_alloc(vfs_superblock_cache, GFP_KERNEL); - if (!sb) { - pr_debug("Cannot allocate memory for the superblock.\n"); - } else { - // Copy the name. - strcpy(sb->name, new_fs_root->name); - // Copy the path. - strcpy(sb->path, path); - // Set the pointer. - sb->root = new_fs_root; - // Add to the list. - list_head_insert_after(&sb->mounts, &vfs_super_blocks); - } - spinlock_unlock(&vfs_spinlock); - pr_debug("Correctly mounted '%s' on '%s'...\n", new_fs_root->name, path); - return 1; -} - -int do_mount(const char *type, const char *path, const char *args) +int vfs_mount(const char *type, const char *path, const char *args) { file_system_type *fst = (file_system_type *)hashmap_get(vfs_filesystems, type); if (fst == NULL) { @@ -489,23 +510,28 @@ int do_mount(const char *type, const char *path, const char *args) pr_err("No mount callback set: %s\n", type); return -ENODEV; } - vfs_file_t *file = fst->mount(path, args); + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + int resolve_flags = 0; + int ret = resolve_path(args, absolute_path, PATH_MAX, resolve_flags); + if (ret < 0) { + pr_err("vfs_mount(type: %s, path: %s, args: %s): Cannot get the absolute path\n", + fst->name, path, args); + return ret; + } + pr_debug("vfs_mount(type: %s, path: %s, args: %s (%s))\n", fst->name, path, args, absolute_path); + vfs_file_t *file = fst->mount(path, absolute_path); if (file == NULL) { pr_err("Mount callback return a null pointer: %s\n", type); return -ENODEV; } - if (!vfs_mount(path, file)) { - pr_err("do_mount(`%s`, `%s`, `%s`) : failed to mount.\n", type, path, args); - return -ENODEV; - } - super_block_t *sb = vfs_get_superblock(path); - if (sb == NULL) { - pr_err("do_mount(`%s`, `%s`, `%s`) : Cannot find the superblock.\n", type, path, args); + // Register the proc superblock. + if (!vfs_register_superblock(file->name, path, fst, file)) { + pr_alert("Failed to register %s superblock!\n", file->name); return -ENODEV; } - // Set the filesystem type. - sb->type = fst; - pr_debug("Mounted %s[%s] to `%s`: file = %p\n", type, args, path, file); + pr_debug("vfs_mount(type: %s, path: %s, args: %s), file: %s\n", fst->name, path, args, file->name); return 0; } diff --git a/mentos/src/io/video.c b/mentos/src/io/video.c index 90235498..5fd8abb7 100644 --- a/mentos/src/io/video.c +++ b/mentos/src/io/video.c @@ -7,6 +7,7 @@ #include "ctype.h" #include "io/vga/vga.h" #include "io/video.h" +#include "io/debug.h" #include "stdbool.h" #include "stdio.h" #include "string.h" @@ -27,6 +28,8 @@ struct ansi_color_map_t { } /// @brief The mapping. ansi_color_map[] = { + { 0, 7 }, + { 30, 0 }, { 31, 4 }, { 32, 2 }, @@ -61,9 +64,7 @@ ansi_color_map[] = { { 104, 9 }, { 105, 13 }, { 106, 11 }, - { 107, 15 }, - - { 0, 0 } + { 107, 15 } }; /// Pointer to a position of the screen writer. @@ -111,17 +112,18 @@ static inline void __draw_char(char c) /// @param ansi_code The ansi code describing background and foreground color. static inline void __set_color(uint8_t ansi_code) { - struct ansi_color_map_t *it = ansi_color_map; - while (it->ansi_color != 0) { - if (ansi_code == it->ansi_color) { - if (((ansi_code >= 30) && (ansi_code <= 37)) || ((ansi_code >= 90) && (ansi_code <= 97))) { - color = (color & 0xF0U) | it->video_color; + for (size_t i = 0; i < count_of(ansi_color_map); ++i) { + if (ansi_code == ansi_color_map[i].ansi_color) { + if ( + (ansi_code == 0) || + ((ansi_code >= 30) && (ansi_code <= 37)) || + ((ansi_code >= 90) && (ansi_code <= 97))) { + color = (color & 0xF0U) | ansi_color_map[i].video_color; } else { - color = (color & 0x0FU) | (it->video_color << 4U); + color = (color & 0x0FU) | (ansi_color_map[i].video_color << 4U); } break; } - ++it; } } diff --git a/mentos/src/ipc/ipc.c b/mentos/src/ipc/ipc.c index 03862443..6076bbfd 100644 --- a/mentos/src/ipc/ipc.c +++ b/mentos/src/ipc/ipc.c @@ -6,6 +6,7 @@ #include "ipc/ipc.h" #include "assert.h" +#include "sys/stat.h" #include "fcntl.h" #include "io/debug.h" #include "process/scheduler.h" diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index 803e0cb4..8336c840 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -237,7 +237,7 @@ int kmain(boot_info_t *boot_informations) //========================================================================== pr_notice("Mount EXT2 filesystem...\n"); printf("Mount EXT2 filesystem..."); - if (do_mount("ext2", "/", "/dev/hda")) { + if (vfs_mount("ext2", "/", "/dev/hda")) { pr_emerg("Failed to mount EXT2 filesystem...\n"); return 1; } @@ -266,7 +266,7 @@ int kmain(boot_info_t *boot_informations) //========================================================================== pr_notice(" Mounting 'procfs'...\n"); printf(" Mounting 'procfs'..."); - if (do_mount("procfs", "/proc", NULL)) { + if (vfs_mount("procfs", "/proc", NULL)) { pr_emerg("Failed to mount procfs at `/proc`!\n"); return 1; } diff --git a/mentos/src/klib/string.c b/mentos/src/klib/string.c index 7579da94..f33c35ce 100644 --- a/mentos/src/klib/string.c +++ b/mentos/src/klib/string.c @@ -5,7 +5,7 @@ #include "string.h" #include "ctype.h" -#include "fcntl.h" +#include "sys/stat.h" #include "stdio.h" #include "stdlib.h" @@ -218,27 +218,35 @@ char *strpbrk(const char *string, const char *control) return NULL; } -int tokenize(const char *string, char *separators, size_t *offset, char *buffer, ssize_t buflen) +int tokenize(const char *string, const char *separators, size_t *offset, char *buffer, ssize_t buflen) { // If we reached the end of the parsed string, stop. if ((*offset >= buflen) || (string[*offset] == 0)) { return 0; } + // Skip any leading (multiple) separators. + while (string[*offset] != 0 && strchr(separators, string[*offset])) { + ++(*offset); + } + // If we reach the end after skipping, return 0. + if (string[*offset] == 0) { + return 0; + } // Keep copying character until we either reach 1) the end of the buffer, 2) a // separator, or 3) the end of the string we are parsing. do { - for (char *separator = separators; *separator != 0; ++separator) { - if (string[*offset] == *separator) { - // Skip the character. - ++(*offset); - // Close the buffer. - *buffer = '\0'; - return 1; - } + // Check if the character is a separator. + if (strchr(separators, string[*offset])) { + // Skip the character. + ++(*offset); + // Close the buffer. + *buffer = '\0'; + return 1; } // Save the character. *buffer = string[*offset]; - // Advance the offset, decrese the available size in the buffer, and advance the buffer. + // Advance the offset, decrese the available size in the buffer, and advance + // the buffer. ++(*offset), --buflen, ++buffer; } while ((buflen > 0) && (string[*offset] != 0)); // Close the buffer. @@ -488,14 +496,14 @@ char *strcpy(char *dst, const char *src) size_t strlen(const char *s) { const char *it = s; - for(; *it; it++); + for (; *it; it++); return (size_t)(it - s); } size_t strnlen(const char *s, size_t count) { const char *p = memchr(s, 0, count); - return p ? (size_t)(p-s) : count; + return p ? (size_t)(p - s) : count; } int strcmp(const char *s1, const char *s2) diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index b569493c..be9f408c 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -12,7 +12,7 @@ #include "assert.h" #include "elf/elf.h" #include "fcntl.h" -#include "fs/vfs.h" +#include "sys/stat.h" #include "hardware/timer.h" #include "klib/stack_helper.h" #include "libgen.h" @@ -23,6 +23,8 @@ #include "string.h" #include "sys/errno.h" #include "system/panic.h" +#include "fs/vfs.h" +#include "fs/namei.h" /// Cache for creating the task structs. static kmem_cache_t *task_struct_cache; @@ -117,7 +119,8 @@ static int __reset_process(task_struct *task) /// @brief Checks if the file starts with a shebang. /// @param file the file to check. /// @return 1 if it contains a shebang, 0 otherwise. -static int __has_shebang(vfs_file_t *file) { +static int __has_shebang(vfs_file_t *file) +{ char buf[2]; vfs_read(file, buf, 0, sizeof(buf)); return buf[0] == '#' && buf[1] == '!'; @@ -131,7 +134,7 @@ static int __has_shebang(vfs_file_t *file) { static int __load_executable(const char *path, task_struct *task, uint32_t *entry) { // Return code variable. - int ret = 0; + int ret = 0; int interpreter_loop = 0; start: pr_debug("__load_executable(`%s`, %p `%s`, %p)\n", path, task, task->name, entry); @@ -179,14 +182,14 @@ static int __load_executable(const char *path, task_struct *task, uint32_t *entr if (interpreter_loop) { ret = -ELOOP; // Free interpreter buffer - kfree((void*)path); + kfree((void *)path); goto close_and_return; } // Read shebang line char buf[PATH_MAX]; ssize_t bytes_read = vfs_read(file, buf, 2, sizeof(buf)); - buf[bytes_read] = 0; + buf[bytes_read] = 0; vfs_close(file); // Find end of the line @@ -210,7 +213,7 @@ static int __load_executable(const char *path, task_struct *task, uint32_t *entr // Free potential interpreter path if (interpreter_loop) { // Free interpreter buffer - kfree((void*)path); + kfree((void *)path); ret = 2; } @@ -440,9 +443,9 @@ int sys_chdir(char const *path) return -EFAULT; } char absolute_path[PATH_MAX]; - if (!realpath(path, absolute_path, sizeof(absolute_path))) { + if (resolve_path(path, absolute_path, sizeof(absolute_path), REMOVE_TRAILING_SLASH | FOLLOW_LINKS) < 0) { pr_err("Cannot get the absolute path for path `%s`.\n", path); - return -ENOENT; + return -errno; } // Check that the directory exists. vfs_file_t *dir = vfs_open(absolute_path, O_RDONLY | O_DIRECTORY, S_IXUSR); @@ -474,7 +477,7 @@ int sys_fchdir(int fd) return -ENOTDIR; } char absolute_path[PATH_MAX]; - if (!realpath(vfd->file_struct->name, absolute_path, sizeof(absolute_path))) { + if (resolve_path(vfd->file_struct->name, absolute_path, sizeof(absolute_path), REMOVE_TRAILING_SLASH | FOLLOW_LINKS) < 0) { pr_err("Cannot get the absolute path for path `%s`.\n", vfd->file_struct->name); return -ENOENT; } @@ -507,9 +510,9 @@ pid_t sys_fork(pt_regs *f) proc->sid = current->sid; proc->pgid = current->pgid; proc->uid = current->uid; - proc->ruid = current->ruid; + proc->ruid = current->ruid; proc->gid = current->gid; - proc->rgid = current->rgid; + proc->rgid = current->rgid; // Active the new process. scheduler_enqueue_task(proc); @@ -599,7 +602,7 @@ int sys_execve(pt_regs *f) // The original file name must be passed as second argument and the rest // is shifted to the right. // Prepare a new argv array. - char **int_argv = kmalloc((argc + 2) * sizeof(char*)); + char **int_argv = kmalloc((argc + 2) * sizeof(char *)); if (!int_argv) { pr_err("Failed to allocate memory for interpreter argv array.\n"); return -1; @@ -607,7 +610,7 @@ int sys_execve(pt_regs *f) int_argv[0] = saved_argv[0]; // TODO: pass the path to the interpreter. int_argv[1] = saved_filename; for (int i = 1; i <= argc; i++) { - int_argv[i+1] = saved_argv[i]; + int_argv[i + 1] = saved_argv[i]; } argc++; @@ -621,8 +624,8 @@ int sys_execve(pt_regs *f) } // Copy the arguments. uint32_t int_args_mem_ptr = (uint32_t)int_args_mem + (int_argv_bytes + envp_bytes); - saved_argv = __push_args_on_stack(&int_args_mem_ptr, int_argv); - saved_envp = __push_args_on_stack(&int_args_mem_ptr, saved_envp); + saved_argv = __push_args_on_stack(&int_args_mem_ptr, int_argv); + saved_envp = __push_args_on_stack(&int_args_mem_ptr, saved_envp); // Check the memory pointer. assert(int_args_mem_ptr == (uint32_t)int_args_mem); // Free the old argument and environ memory block. diff --git a/programs/login.c b/programs/login.c index d5fce55f..a72c9134 100644 --- a/programs/login.c +++ b/programs/login.c @@ -184,7 +184,6 @@ int main(int argc, char **argv) do { printf("Username: "); } while (!__get_input(username, sizeof(username), false)); - // Get the password. do { printf("Password: "); diff --git a/programs/ls.c b/programs/ls.c index 2f251444..9b316667 100644 --- a/programs/ls.c +++ b/programs/ls.c @@ -15,19 +15,40 @@ #include #include #include +#include #define FLAG_L (1U << 0U) #define FLAG_A (1U << 1U) #define FLAG_I (1U << 2U) #define FLAG_1 (1U << 3U) -#define FG_BRIGHT_GREEN "\033[92m" -#define FG_BRIGHT_CYAN "\033[96m" -#define FG_BRIGHT_WHITE "\033[97m" -#define FG_BRIGHT_YELLOW "\033[93m" - #define DENTS_NUM 12 +static inline void print_dir_entry_name(const char *name, mode_t st_mode) +{ + if (S_ISSOCK(st_mode)) { + printf(FG_YELLOW_BRIGHT "%s" FG_RESET, name); + } + if (S_ISLNK(st_mode)) { + printf(FG_CYAN_BRIGHT "%s" FG_RESET, name); + } + if (S_ISREG(st_mode)) { + printf(FG_WHITE_BRIGHT "%s" FG_RESET, name); + } + if (S_ISBLK(st_mode)) { + printf(FG_GREEN_BRIGHT "%s" FG_RESET, name); + } + if (S_ISDIR(st_mode)) { + printf(FG_BLUE_BRIGHT "%s" FG_RESET, name); + } + if (S_ISCHR(st_mode)) { + printf(FG_YELLOW_BRIGHT "%s" FG_RESET, name); + } + if (S_ISFIFO(st_mode)) { + printf(FG_YELLOW_BRIGHT "%s" FG_RESET, name); + } +} + static inline void print_dir_entry(dirent_t *dirent, const char *path, unsigned int flags, size_t *total_size) { static char relative_path[PATH_MAX]; @@ -42,24 +63,17 @@ static inline void print_dir_entry(dirent_t *dirent, const char *path, unsigned // Prepare the relative path. strcpy(relative_path, path); - if (strcmp(path, "/") != 0) + if (path[strnlen(path, PATH_MAX) - 1] != '/') { strcat(relative_path, "/"); + } strcat(relative_path, dirent->d_name); // Stat the file. - if (stat(relative_path, &dstat) == -1) { + if (stat(relative_path, &dstat) < 0) { + printf("ls: failed to stat `%s`\n", relative_path); return; } - // Deal with the coloring. - if ((dirent->d_type == DT_REG) && bitmask_check(dstat.st_mode, S_IXUSR)) { - puts(FG_BRIGHT_YELLOW); - } else if (dirent->d_type == DT_DIR) { - puts(FG_BRIGHT_CYAN); - } else if (dirent->d_type == DT_BLK) { - puts(FG_BRIGHT_GREEN); - } - // Deal with the -l. if (bitmask_check(flags, FLAG_L)) { // Get the broken down time from the creation time of the file. @@ -83,22 +97,33 @@ static inline void print_dir_entry(dirent_t *dirent, const char *path, unsigned // Add a space. putchar(' '); // Print the rest. - printf("%4d %4d %11s %02d/%02d %02d:%02d %s\n", + printf("%4d %4d %11s %02d/%02d %02d:%02d ", dstat.st_uid, dstat.st_gid, to_human_size(dstat.st_size), timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, - timeinfo->tm_min, - dirent->d_name); + timeinfo->tm_min); + + print_dir_entry_name(dirent->d_name, dstat.st_mode); + + if (S_ISLNK(dstat.st_mode)) { + char link_buffer[PATH_MAX]; + ssize_t len = readlink(relative_path, link_buffer, sizeof(link_buffer)); + if (len > 0) { + link_buffer[len] = '\0'; + printf(" -> %s", link_buffer); + } + } + putchar('\n'); (*total_size) += dstat.st_size; } else { // Print the inode if required. if (bitmask_check(flags, FLAG_I)) { printf("%d ", dirent->d_ino); } - puts(dirent->d_name); + print_dir_entry_name(dirent->d_name, dstat.st_mode); // Print in 1 column if requested. if (bitmask_check(flags, FLAG_1)) { putchar('\n'); @@ -106,17 +131,21 @@ static inline void print_dir_entry(dirent_t *dirent, const char *path, unsigned putchar(' '); } } - - // Reset the color. - puts(FG_BRIGHT_WHITE); } -static void print_ls(int fd, const char *path, unsigned int flags) +static void print_ls(const char *path, unsigned int flags) { + // Open the directory. + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("ls: cannot access '%s': %s\n", path, strerror(errno)); + return; + } + + // Clear the directory entry buffer. dirent_t dents[DENTS_NUM]; memset(&dents, 0, DENTS_NUM * sizeof(dirent_t)); - - size_t total_size = 0; + size_t total_size = 0; ssize_t bytes_read = 0; while ((bytes_read = getdents(fd, dents, sizeof(dents))) > 0) { for (size_t i = 0; i < bytes_read / sizeof(dirent_t); ++i) { @@ -126,11 +155,10 @@ static void print_ls(int fd, const char *path, unsigned int flags) if (bytes_read < 0) { perror("getdents failed"); } - printf("\n"); - if (bitmask_check(flags, FLAG_L)) { printf("Total: %s\n", to_human_size(total_size)); } + close(fd); } int main(int argc, char *argv[]) @@ -164,31 +192,20 @@ int main(int argc, char *argv[]) bitmask_set_assign(flags, FLAG_I); } } - bool_t no_directory = true; for (int i = 1; i < argc; ++i) { - if (argv[i][0] == '-') - continue; - no_directory = false; - int fd = open(argv[i], O_RDONLY | O_DIRECTORY, 0); - if (fd == -1) { - printf("ls: cannot access '%s': %s\n", argv[i], strerror(errno)); - } else { + if (argv[i][0] != '-') { + no_directory = false; printf("%s:\n", argv[i]); - print_ls(fd, argv[i], flags); - close(fd); + print_ls(argv[i], flags); + printf("\n"); } } if (no_directory) { char cwd[PATH_MAX]; getcwd(cwd, PATH_MAX); - int fd = open(cwd, O_RDONLY | O_DIRECTORY, 0); - if (fd == -1) { - printf("ls: cannot access '%s': %s\n", cwd, strerror(errno)); - } else { - print_ls(fd, cwd, flags); - close(fd); - } + print_ls(cwd, flags); + printf("\n"); } return 0; } diff --git a/programs/ps.c b/programs/ps.c index 445476ca..33da7a06 100644 --- a/programs/ps.c +++ b/programs/ps.c @@ -9,10 +9,30 @@ #include #include #include +#include #define FORMAT_S "%5s %5s %6s %s\n" #define FORMAT "%5d %5d %6c %s\n" +static inline int __is_number(const char *str) +{ + // Check for NULL pointer. + if (str == NULL) { return 0; } + // Skip leading whitespace. + while (isspace((unsigned char)*str)) { str++; } + // Check if there is a sign at the beginning. + if (*str == '-' || *str == '+') { str++; } + // Check if the string is empty or only contains a sign. + if (*str == '\0') { return 0; } + // Check each character to ensure it's a digit + while (*str) { + if (!isdigit((unsigned char)*str)) { return 0; } + str++; + } + // If we reach here, all characters were digits. + return 1; +} + static inline void __iterate_proc_dirs(int proc_fd) { char absolute_path[PATH_MAX] = "/proc/"; @@ -49,6 +69,10 @@ static inline void __iterate_proc_dirs(int proc_fd) if (dent.d_type != DT_DIR) { continue; } + // Skip directories that are not PIDs. + if (!__is_number(dent.d_name)) { + continue; + } // Build the path to the stat file (i.e., `/proc//stat`). strcpy(absolute_path + 6, dent.d_name); strcat(absolute_path, "/stat"); diff --git a/programs/shell.c b/programs/shell.c index 2ed0abba..7025e9a8 100644 --- a/programs/shell.c +++ b/programs/shell.c @@ -177,7 +177,7 @@ static inline void __prompt_print(void) } else { HOSTNAME = buffer.nodename; } - printf(FG_GREEN "%s" FG_WHITE "@" FG_CYAN "%s " FG_BLUE_BRIGHT "[%02d:%02d:%02d]" FG_WHITE " [%s] " FG_WHITE_BRIGHT "\n-> %% ", + printf(FG_GREEN "%s" FG_WHITE "@" FG_CYAN "%s " FG_BLUE_BRIGHT "[%02d:%02d:%02d]" FG_WHITE " [%s] " FG_RESET "\n-> %% ", USER, HOSTNAME, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, CWD); } @@ -353,6 +353,28 @@ static int __cd(int argc, char *argv[]) printf("cd: Failed to resolve directory.\n"); return 1; } + // Stat the directory. + stat_t dstat; + if (stat(real_path, &dstat) == -1) { + printf("cd: cannot stat '%s': %s\n", real_path, strerror(errno)); + return 1; + } + // Check if the directory is actually a symbolic link. + if (S_ISLNK(dstat.st_mode)) { + char link_buffer[PATH_MAX]; + ssize_t len; + // First, read the link. + if ((len = readlink(real_path, link_buffer, sizeof(link_buffer))) < 0) { + printf("cd: Failed to read symlink.\n"); + return 1; + } + link_buffer[len] = '\0'; + // Resolve the link, it might still be a relative path. + if (realpath(link_buffer, real_path, PATH_MAX) != real_path) { + printf("cd: Failed to resolve symlink to directory.\n"); + return 1; + } + } // Open the given directory. int fd = open(real_path, O_RDONLY | O_DIRECTORY, S_IXUSR); if (fd == -1) { diff --git a/programs/stat.c b/programs/stat.c index f5834231..456659db 100644 --- a/programs/stat.c +++ b/programs/stat.c @@ -15,17 +15,6 @@ #include #include -// Copied from mentos/src/fs/ext2.c -// File types. -#define S_IFMT 0xF000 ///< Format mask -#define S_IFSOCK 0xC000 ///< Socket -#define S_IFLNK 0xA000 ///< Symbolic link -#define S_IFREG 0x8000 ///< Regular file -#define S_IFBLK 0x6000 ///< Block device -#define S_IFDIR 0x4000 ///< Directory -#define S_IFCHR 0x2000 ///< Character device -#define S_IFIFO 0x1000 ///< Fifo - static void __print_time(const char *prefix, time_t *time) { tm_t *timeinfo = localtime(time); @@ -51,51 +40,61 @@ int main(int argc, char **argv) printf("Display file status.\n"); exit(0); } - stat_t statbuf; - if (stat(argv[1], &statbuf) == -1) { + stat_t dstat; + if (stat(argv[1], &dstat) == -1) { printf("%s: cannot stat '%s': %s\n", argv[0], argv[1], strerror(errno)); exit(1); } - printf("File: %s\n", argv[1]); - printf("Size: %s\n", to_human_size(statbuf.st_size)); + printf("File: %s", argv[1]); + if (S_ISLNK(dstat.st_mode)) { + char link_buffer[PATH_MAX]; + ssize_t len = readlink(argv[1], link_buffer, sizeof(link_buffer)); + if (len > 0) { + link_buffer[len] = '\0'; + printf(" -> %s", link_buffer); + } + } + putchar('\n'); + printf("Size: %12s ", to_human_size(dstat.st_size)); + printf("Inode: %d\n", dstat.st_ino); printf("File type: "); - switch (statbuf.st_mode & S_IFMT) { + switch (dstat.st_mode & S_IFMT) { case S_IFBLK : printf("block device\n"); break; case S_IFCHR : printf("character device\n"); break; case S_IFDIR : printf("directory\n"); break; - case S_IFIFO : printf("FIFO/pipe\n"); break; - case S_IFLNK : printf("symlink\n"); break; + case S_IFIFO : printf("fifo/pipe\n"); break; + case S_IFLNK : printf("symbolic link\n"); break; case S_IFREG : printf("regular file\n"); break; case S_IFSOCK: printf("socket\n"); break; default : printf("unknown?\n"); break; } - printf("Access: (%.4o/", statbuf.st_mode & 0xFFF); + printf("Access: (%.4o/", dstat.st_mode & 0xFFF); // Print the access permissions. - putchar(bitmask_check(statbuf.st_mode, S_IRUSR) ? 'r' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IWUSR) ? 'w' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IXUSR) ? 'x' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IRGRP) ? 'r' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IWGRP) ? 'w' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IXGRP) ? 'x' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IROTH) ? 'r' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IWOTH) ? 'w' : '-'); - putchar(bitmask_check(statbuf.st_mode, S_IXOTH) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IRUSR) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWUSR) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXUSR) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IRGRP) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWGRP) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXGRP) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IROTH) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWOTH) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXOTH) ? 'x' : '-'); - passwd_t *user = getpwuid(statbuf.st_uid); + passwd_t *user = getpwuid(dstat.st_uid); if (!user) { - printf("%s: failed to retrieve uid '%u'.\n", argv[0], statbuf.st_uid); + printf("%s: failed to retrieve uid '%u'.\n", argv[0], dstat.st_uid); exit(1); } - group_t *group = getgrgid(statbuf.st_gid); + group_t *group = getgrgid(dstat.st_gid); if (!group) { - printf("%s: failed to retrieve gid '%u'.\n", argv[0], statbuf.st_gid); + printf("%s: failed to retrieve gid '%u'.\n", argv[0], dstat.st_gid); exit(1); } - printf(") Uid: (%d/%s) Gid: (%d/%s)\n", statbuf.st_uid, user->pw_name, statbuf.st_gid, group->gr_name); + printf(") Uid: (%d/%s) Gid: (%d/%s)\n", dstat.st_uid, user->pw_name, dstat.st_gid, group->gr_name); - __print_time("Access: ", &statbuf.st_atime); - __print_time("Modify: ", &statbuf.st_mtime); - __print_time("Change: ", &statbuf.st_ctime); + __print_time("Access: ", &dstat.st_atime); + __print_time("Modify: ", &dstat.st_mtime); + __print_time("Change: ", &dstat.st_ctime); return 0; } diff --git a/programs/tests/t_dup.c b/programs/tests/t_dup.c index 9681e1e8..5bc2171e 100644 --- a/programs/tests/t_dup.c +++ b/programs/tests/t_dup.c @@ -6,13 +6,14 @@ #include #include #include +#include #include #include #include int main(int argc, char *argv[]) { - char *file = "t_dup_file"; + const 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; diff --git a/programs/tests/t_msgget.c b/programs/tests/t_msgget.c index 1d3e9331..e4f2e5bb 100644 --- a/programs/tests/t_msgget.c +++ b/programs/tests/t_msgget.c @@ -3,14 +3,15 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "string.h" -#include "sys/unistd.h" -#include "sys/errno.h" -#include "sys/msg.h" -#include "sys/ipc.h" -#include "stdlib.h" -#include "fcntl.h" -#include "stdio.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #define MESSAGE_LEN 100 diff --git a/programs/tests/t_semflg.c b/programs/tests/t_semflg.c index bb42b22e..a20822ab 100644 --- a/programs/tests/t_semflg.c +++ b/programs/tests/t_semflg.c @@ -3,13 +3,14 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/unistd.h" -#include "sys/errno.h" -#include "sys/sem.h" -#include "sys/ipc.h" -#include "stdlib.h" -#include "fcntl.h" -#include "stdio.h" +#include +#include +#include +#include +#include +#include +#include +#include int main(int argc, char *argv[]) { diff --git a/programs/tests/t_semget.c b/programs/tests/t_semget.c index ccfc81e2..a38e546c 100644 --- a/programs/tests/t_semget.c +++ b/programs/tests/t_semget.c @@ -5,13 +5,14 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/unistd.h" -#include "sys/errno.h" -#include "sys/sem.h" -#include "sys/ipc.h" -#include "stdlib.h" -#include "fcntl.h" -#include "stdio.h" +#include +#include +#include +#include +#include +#include +#include +#include int main(int argc, char *argv[]) { diff --git a/programs/tests/t_semop.c b/programs/tests/t_semop.c index 23606fe2..022cdc48 100644 --- a/programs/tests/t_semop.c +++ b/programs/tests/t_semop.c @@ -3,14 +3,15 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/sem.h" -#include "stdio.h" -#include "sys/ipc.h" -#include "sys/errno.h" -#include "sys/wait.h" -#include "stdlib.h" -#include "sys/unistd.h" -#include "fcntl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include int main(int argc, char *argv[]) { diff --git a/programs/tests/t_write_read.c b/programs/tests/t_write_read.c index 1b41ff3e..c491d6e3 100644 --- a/programs/tests/t_write_read.c +++ b/programs/tests/t_write_read.c @@ -44,6 +44,7 @@ int check_content(const char *filename, const char *content, int length) close(fd); return EXIT_FAILURE; } + close(fd); return EXIT_SUCCESS; } diff --git a/programs/touch.c b/programs/touch.c index 3eeb623d..22a8d89f 100644 --- a/programs/touch.c +++ b/programs/touch.c @@ -8,9 +8,10 @@ #include #include #include +#include #include -int main(int argc, char** argv) +int main(int argc, char **argv) { if (argc != 2) { printf("%s: missing operand.\n", argv[0]); @@ -23,13 +24,12 @@ int main(int argc, char** argv) printf(" touch \n"); return 0; } - int fd = open(argv[1], O_RDONLY, 0); + int fd = open(argv[1], O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0) { - fd = open(argv[1], O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); - if (fd < 0) { - err(EXIT_FAILURE, "cannot touch %s", argv[1]); - } - close(fd); + err(EXIT_FAILURE, "cannot touch %s", argv[1]); + printf("\n"); } + close(fd); + printf("\n"); return 0; }