From 1ebc74f31f4dca7eab6e1fbbefc5bba091a10f26 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Mon, 27 Dec 2021 17:41:35 +0100 Subject: [PATCH] Add creat function call. Fix direntry iteration in EXT2. Fix open of directories in EXT2. Fix re-utilization of unlinked direntries in EXT2. --- libc/CMakeLists.txt | 1 + libc/inc/sys/stat.h | 10 +- libc/inc/sys/unistd.h | 11 +- libc/src/unistd/creat.c | 11 ++ mentos/inc/fs/ext2.h | 2 + mentos/inc/fs/vfs.h | 8 + mentos/inc/fs/vfs_types.h | 4 + mentos/inc/system/syscall.h | 8 + mentos/src/fs/ext2.c | 317 ++++++++++++++++++++++-------------- mentos/src/fs/namei.c | 44 ++++- mentos/src/fs/vfs.c | 53 +++++- mentos/src/kernel.c | 2 + mentos/src/system/syscall.c | 1 + programs/shell.c | 2 +- 14 files changed, 330 insertions(+), 144 deletions(-) create mode 100644 libc/src/unistd/creat.c diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 0d26c45..29459bc 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -84,6 +84,7 @@ add_library( ${PROJECT_SOURCE_DIR}/src/sys/errno.c ${PROJECT_SOURCE_DIR}/src/sys/utsname.c ${PROJECT_SOURCE_DIR}/src/sys/ioctl.c + ${PROJECT_SOURCE_DIR}/src/unistd/creat.c ${PROJECT_SOURCE_DIR}/src/unistd/getppid.c ${PROJECT_SOURCE_DIR}/src/unistd/getpid.c ${PROJECT_SOURCE_DIR}/src/unistd/exit.c diff --git a/libc/inc/sys/stat.h b/libc/inc/sys/stat.h index 39fb7b2..258087f 100644 --- a/libc/inc/sys/stat.h +++ b/libc/inc/sys/stat.h @@ -32,4 +32,12 @@ int mkdir(const char *path, mode_t mode); /// @brief Removes the given directory. /// @param path The path to the directory to remove. /// @return Returns a negative value on failure. -int rmdir(const char *path); \ No newline at end of file +int rmdir(const char *path); + +/// @brief Creates a new file or rewrite an existing one. +/// @param path path to the file. +/// @param mode mode for file creation. +/// @return file descriptor number, -1 otherwise and errno is set to indicate the error. +/// @details +/// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) +int creat(const char *path, mode_t mode); \ No newline at end of file diff --git a/libc/inc/sys/unistd.h b/libc/inc/sys/unistd.h index 63bf18c..d14edcf 100644 --- a/libc/inc/sys/unistd.h +++ b/libc/inc/sys/unistd.h @@ -28,14 +28,11 @@ ssize_t read(int fd, void *buf, size_t nbytes); /// @return The number of written bytes. ssize_t write(int fd, void *buf, size_t nbytes); -/// @brief Opens the file specified by pathname. +/// @brief Opens the file specified by pathname. /// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer for -/// use in subsequent system calls. +/// @param flags file status flags and file access modes of the open file description. +/// @param mode the file mode bits be applied when a new file is created. +/// @return file descriptor number, -1 otherwise and errno is set to indicate the error. int open(const char *pathname, int flags, mode_t mode); /// @brief Close a file descriptor. diff --git a/libc/src/unistd/creat.c b/libc/src/unistd/creat.c new file mode 100644 index 0000000..2e91f40 --- /dev/null +++ b/libc/src/unistd/creat.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file creat.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(int, creat, const char *, pathname, mode_t, mode) diff --git a/mentos/inc/fs/ext2.h b/mentos/inc/fs/ext2.h index 0968c85..dfc3447 100644 --- a/mentos/inc/fs/ext2.h +++ b/mentos/inc/fs/ext2.h @@ -15,3 +15,5 @@ int ext2_initialize(); /// @brief De-initializes the EXT2 drivers. /// @return 0 on success, 1 on error. int ext2_finalize(); + +void ext2_test(); \ No newline at end of file diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index 1a2a5e1..4852c28 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -112,6 +112,14 @@ int vfs_mkdir(const char *path, mode_t mode); /// @return Returns a negative value on failure. int vfs_rmdir(const char *path); +/// @brief Creates a new file or rewrite an existing one. +/// @param path path to the file. +/// @param mode mode for file creation. +/// @return file descriptor number, -1 otherwise and errno is set to indicate the error. +/// @details +/// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) +vfs_file_t *vfs_creat(const char *path, mode_t mode); + /// @brief Stat the file at the given path. /// @param path Path to the file for which we are retrieving the statistics. /// @param buf Buffer where we are storing the statistics. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index 7141fe5..2ae2bcc 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -23,6 +23,8 @@ typedef struct vfs_file_t vfs_file_t; typedef int (*vfs_mkdir_callback)(const char *, mode_t); /// Function used to remove a directory. typedef int (*vfs_rmdir_callback)(const char *); +/// Function used to open a file (or directory). +typedef vfs_file_t *(*vfs_creat_callback)(const char *, mode_t); /// Function used to read the entries of a directory. typedef int (*vfs_getdents_callback)(vfs_file_t *, dirent_t *, off_t, size_t); /// Function used to open a file (or directory). @@ -62,6 +64,8 @@ typedef struct vfs_sys_operations_t { vfs_rmdir_callback rmdir_f; /// Stat function. vfs_stat_callback stat_f; + /// File creation function. + vfs_creat_callback creat_f; } vfs_sys_operations_t; /// @brief Set of functions used to perform operations on files. diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index b0063b7..ac23c1f 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -208,6 +208,14 @@ int sys_mkdir(const char *path, mode_t mode); /// @return Returns a negative value on failure. int sys_rmdir(const char *path); +/// @brief Creates a new file or rewrite an existing one. +/// @param path path to the file. +/// @param mode mode for file creation. +/// @return file descriptor number, -1 otherwise and errno is set to indicate the error. +/// @details +/// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) +int sys_creat(const char *path, mode_t mode); + /// Provide access to the directory entries. /// @param fd The file descriptor of the directory for which we accessing /// the entries. diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index d46b6cc..b2405a5 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -386,6 +386,7 @@ static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t co static int ext2_mkdir(const char *path, mode_t mode); static int ext2_rmdir(const char *path); static int ext2_stat(const char *path, stat_t *stat); +static vfs_file_t *ext2_creat(const char *path, mode_t permission); static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path); @@ -397,7 +398,8 @@ static vfs_file_t *ext2_mount(vfs_file_t *block_device, const char *path); static vfs_sys_operations_t ext2_sys_operations = { .mkdir_f = ext2_mkdir, .rmdir_f = ext2_rmdir, - .stat_f = ext2_stat + .stat_f = ext2_stat, + .creat_f = ext2_creat }; /// Filesystem file operations. @@ -1653,6 +1655,12 @@ static int ext2_allocate_direntry(ext2_filesystem_t *fs, vfs_file_t *parent, con // Iterate the directory entries. ext2_direntry_iterator_t it = ext2_direntry_iterator_begin(fs, cache, &parent_inode); for (; ext2_direntry_iterator_valid(&it); ext2_direntry_iterator_next(&it)) { + // If we hit a direntry with an empty inode, that is a free direntry. + if (it.direntry->inode == 0) { + pr_debug("Found free direntry: %p (%d <= %d)\n", it.direntry, it.direntry->rec_len, rec_len); + if (rec_len <= it.direntry->rec_len) + break; + } // Compute the real rec_len of the entry. uint32_t real_rec_len = ext2_get_rec_len_from_direntry(it.direntry); // If the previous direntry has a wrong rec_len (wrong because it identifies the last). @@ -1670,12 +1678,29 @@ static int ext2_allocate_direntry(ext2_filesystem_t *fs, vfs_file_t *parent, con // Stop here. break; } - // If we hit a direntry with an empty inode, that is a free direntry. - if (it.direntry->inode == 0) - break; } if (it.direntry) { pr_debug("We need to replace the direntry: %p\n", it.direntry); + // Clean the previous name. + memset(it.direntry->name, 0, it.direntry->name_len); + // Set the inode. + it.direntry->inode = inode_index; + // Set the new name length, + it.direntry->name_len = strlen(name); + // Set the new name. + memcpy(it.direntry->name, name, it.direntry->name_len); + // Set the file type. + it.direntry->file_type = file_type; + + if (ext2_write_inode_block(fs, &parent_inode, parent->ino, it.block_index, cache) == -1) { + pr_err("Failed to update the block of the father directory.\n"); + // Free the cache. + kmem_cache_free(cache); + return -1; + } + // Free the cache. + kmem_cache_free(cache); + return 0; } else if ((it.block_offset + rec_len) >= fs->block_size) { pr_debug("We need a new direntry, and a new block (%d + %d >= %d).\n", it.block_offset, rec_len, fs->block_size); it.block_index += 1; @@ -1759,12 +1784,15 @@ static int ext2_find_direntry(ext2_filesystem_t *fs, ino_t ino, const char *name memset(cache, 0, fs->block_size); ext2_direntry_iterator_t it = ext2_direntry_iterator_begin(fs, cache, &inode); for (; ext2_direntry_iterator_valid(&it); ext2_direntry_iterator_next(&it)) { + // Skip unused inode. + if (it.direntry->inode == 0) + continue; // Chehck the name. if (!strcmp(it.direntry->name, ".") && !strcmp(name, "/")) { break; } // Check if the entry has the same name. - if ((it.direntry->inode != 0) && (strlen(name) == it.direntry->name_len)) + if (strlen(name) == it.direntry->name_len) if (!strncmp(it.direntry->name, name, it.direntry->name_len)) break; } @@ -1936,19 +1964,17 @@ static int ext2_init_vfs_file( const char *name, size_t name_len) { - // Information for root dir. - file->device = (void *)fs; - file->ino = inode_index; // Copy the name memcpy(file->name, name, name_len); file->name[name_len] = 0; - // Information from the inode. - file->uid = inode->uid; - file->gid = inode->gid; - file->length = inode->size; - file->mask = inode->mode & 0xFFF; - file->nlink = inode->links_count; - // File flags. + // Set the device. + file->device = (void *)fs; + // Set the mask. + file->mask = inode->mode & 0xFFF; + // Set UID and GID. + file->uid = inode->uid; + file->gid = inode->gid; + // Set the VFS specific flags. file->flags = 0; if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) { file->flags |= DT_REG; @@ -1968,15 +1994,29 @@ static int ext2_init_vfs_file( if ((inode->mode & EXT2_S_IFLNK) == EXT2_S_IFLNK) { file->flags |= DT_LNK; } - file->atime = inode->atime; - file->mtime = inode->mtime; - file->ctime = inode->ctime; + // Set the inode. + file->ino = inode_index; + // Set the size of the file. + file->length = inode->size; + //uint32_t impl; + //uint32_t open_flags; + // Set the open count. + file->count = 0; + // Set the timing information. + file->atime = inode->atime; + file->mtime = inode->mtime; + file->ctime = inode->ctime; + // Set the FS specific operations. file->sys_operations = &ext2_sys_operations; file->fs_operations = &ext2_fs_operations; + // Set the read offest. + file->f_pos = 0; + // Set the number of links. + file->nlink = inode->links_count; // Initialize the list of siblings. list_head_init(&file->siblings); - - pr_debug("ext2_init_vfs_file : [%d] `%s` (%s) (name len : %d)\n", file->ino, file->name, name, strlen(name)); + // Set the refcount to zero. + file->refcount = 0; return 0; } @@ -2082,24 +2122,41 @@ static int ext2_create_inode( return inode_index; } -static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t permission) +/// @brief Creates a new file or rewrite an existing one. +/// @param path path to the file. +/// @param mode mode for file creation. +/// @return file descriptor number, -1 otherwise and errno is set to indicate the error. +/// @details +/// It is equivalent to: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode) +static vfs_file_t *ext2_creat(const char *path, mode_t permission) { + // Get the name of the directory. + const char *parent_path = dirname(path), *file_name = basename(path); + if (strcmp(parent_path, path) == 0) { + return NULL; + } + // Get the parent VFS node. + vfs_file_t *parent = vfs_open(parent_path, O_RDONLY, 0); + if (parent == NULL) { + errno = ENOENT; + return NULL; + } // flags = O_WRONLY | O_CREAT | O_TRUNC // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)parent->device; if (fs == NULL) { pr_err("The parent does not belong to an EXT2 filesystem `%s`.\n", parent->name); - return NULL; + goto close_parent_return_null; } // Prepare the structure for the direntry. ext2_dirent_t direntry; // Prepare an inode, it will come in handy either way. ext2_inode_t inode; // Search if the entry already exists. - if (!ext2_find_direntry_simple(fs, parent->ino, name, &direntry)) { + if (!ext2_find_direntry_simple(fs, parent->ino, file_name, &direntry)) { if (ext2_read_inode(fs, &inode, direntry.inode) == -1) { pr_err("Failed to read the inode of `%s`.\n", direntry.name); - return NULL; + goto close_parent_return_null; } vfs_file_t *file = ext2_find_vfs_file_with_inode(fs, direntry.inode); if (file == NULL) { @@ -2107,11 +2164,11 @@ static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t permi file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); if (file == NULL) { pr_err("Failed to allocate memory for the EXT2 file.\n"); - return NULL; + goto close_parent_return_null; } if (ext2_init_vfs_file(fs, file, &inode, direntry.inode, direntry.name, direntry.name_len) == -1) { pr_err("Failed to properly set the VFS file.\n"); - return NULL; + goto close_parent_return_null; } // Add the vfs_file to the list of associated files. list_head_add_tail(&file->siblings, &fs->opened_files); @@ -2127,24 +2184,27 @@ static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t permi int inode_index = ext2_create_inode(fs, &inode, mode, group_index); if (inode_index == -1) { pr_err("Failed to create a new inode inside `%s` (group index: %d).\n", parent->name, group_index); - return NULL; + goto close_parent_return_null; } // Initialize the file. - if (ext2_allocate_direntry(fs, parent, name, inode_index, ext2_file_type_regular_file) == -1) { + if (ext2_allocate_direntry(fs, parent, file_name, inode_index, ext2_file_type_regular_file) == -1) { pr_err("Failed to allocate a new direntry for the inode.\n"); - return NULL; + goto close_parent_return_null; } // Allocate the memory for the file. vfs_file_t *new_file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); if (new_file == NULL) { pr_err("Failed to allocate memory for the EXT2 file.\n"); - return NULL; + goto close_parent_return_null; } - if (ext2_init_vfs_file(fs, new_file, &inode, inode_index, name, strlen(name)) == -1) { + if (ext2_init_vfs_file(fs, new_file, &inode, inode_index, file_name, strlen(file_name)) == -1) { pr_err("Failed to properly set the VFS file.\n"); - return NULL; + goto close_parent_return_null; } return new_file; +close_parent_return_null: + vfs_close(parent); + return NULL; } /// @brief Open the file at the given path and returns its file descriptor. @@ -2182,31 +2242,16 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) // First check, if a file with the given name already exists. if (!ext2_resolve_path(fs->root, absolute_path, &search)) { if (bitmask_check(flags, O_CREAT | O_EXCL)) { - pr_err("A file at `%s` already exists (O_CREAT | O_EXCL).\n", absolute_path); + pr_err("A file or directory already exists at `%s` (O_CREAT | O_EXCL).\n", absolute_path); + return NULL; + } else if (bitmask_check(flags, O_DIRECTORY) && (direntry.file_type != ext2_file_type_directory)) { + errno = ENOTDIR; return NULL; } } else { // If we need to create it, it's ok if it does not exist. if (bitmask_check(flags, O_CREAT)) { - // Get the name of the directory. - char *parent_path = dirname(path); - if (strcmp(parent_path, path)) { - // Get the parent VFS node. - vfs_file_t *parent = ext2_open(parent_path, O_RDONLY, 0); - if (parent) { - // Create the file. - vfs_file_t *new_file = ext2_creat(parent, basename(path), mode); - if (new_file) { - // Add the vfs_file to the list of associated files. - list_head_add_tail(&new_file->siblings, &fs->opened_files); - } - // Close the parent directory. - ext2_close(parent); - // Return the newly created file. - return new_file; - } - } - return NULL; + return ext2_creat(path, mode); } else { pr_err("The file does not exist `%s`.\n", absolute_path); return NULL; @@ -2522,6 +2567,9 @@ static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t co // Initialize the iterator. ext2_direntry_iterator_t it = ext2_direntry_iterator_begin(fs, cache, &inode); for (; ext2_direntry_iterator_valid(&it) && (written < count); ext2_direntry_iterator_next(&it)) { + // Skip unused inode. + if (it.direntry->inode == 0) + continue; // Skip if already provided. current += sizeof(dirent_t); if (current <= doff) @@ -2566,29 +2614,8 @@ static int ext2_mkdir(const char *path, mode_t permission) ext2_inode_t inode; // Search if the entry already exists. if (!ext2_resolve_path_direntry(fs->root, absolute_path, &direntry)) { - // Get the associated inode. - if (ext2_read_inode(fs, &inode, direntry.inode) == -1) { - pr_err("Failed to read the inode of `%s`.\n", direntry.name); - return -ENOENT; - } - // Create a VFS file. - vfs_file_t *file = ext2_find_vfs_file_with_inode(fs, direntry.inode); - if (file == NULL) { - // 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"); - return -ENOMEM; - } - if (ext2_init_vfs_file(fs, file, &inode, direntry.inode, direntry.name, direntry.name_len) == -1) { - pr_err("Failed to properly set the VFS file.\n"); - return -ENOENT; - } - // Add the vfs_file to the list of associated files. - list_head_add_tail(&file->siblings, &fs->opened_files); - } - ext2_close(file); - return 0; + pr_err("Directory already exists.\n"); + return -EEXIST; } // Get the parent directory. char *parent_path = dirname(path); @@ -2597,7 +2624,7 @@ static int ext2_mkdir(const char *path, mode_t permission) return -ENOENT; } // Get the parent VFS node. - vfs_file_t *parent = ext2_open(parent_path, O_RDONLY, 0); + vfs_file_t *parent = vfs_open(parent_path, O_RDONLY, 0); if (parent == NULL) { pr_err("Failed to open parent directory (%s).\n", parent_path); return -ENOENT; @@ -2612,33 +2639,18 @@ static int ext2_mkdir(const char *path, mode_t permission) if (inode_index == -1) { pr_err("Failed to create a new inode inside `%s` (group index: %d).\n", parent->name, group_index); // Close the parent directory. - ext2_close(parent); + vfs_close(parent); return -ENOENT; } // Initialize the file. if (ext2_allocate_direntry(fs, parent, basename(path), inode_index, ext2_file_type_directory) == -1) { pr_err("Failed to allocate a new direntry for the inode.\n"); // Close the parent directory. - ext2_close(parent); - return -ENOENT; - } - // Allocate the memory for the file. - vfs_file_t *new_file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); - if (new_file == NULL) { - pr_err("Failed to allocate memory for the EXT2 file.\n"); - // Close the parent directory. - ext2_close(parent); - return -ENOMEM; - } - if (ext2_init_vfs_file(fs, new_file, &inode, inode_index, basename(path), strlen(basename(path))) == -1) { - pr_err("Failed to properly set the VFS file.\n"); - // Close the parent directory. - ext2_close(parent); + vfs_close(parent); return -ENOENT; } // Close the parent directory. - ext2_close(parent); - ext2_close(new_file); + vfs_close(parent); return 0; } @@ -3002,6 +3014,7 @@ void dump_dir(vfs_file_t *dir) pr_err("Failed to read the inode (%d).\n", dir->ino); return; } + pr_debug("dir: `%-s` inode: `%4d` {\n", dir->name, dir->ino); // Allocate the cache. uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL); // Clean the cache. @@ -3009,39 +3022,34 @@ void dump_dir(vfs_file_t *dir) // Iterate the directory. ext2_direntry_iterator_t it = ext2_direntry_iterator_begin(fs, cache, &inode); for (; ext2_direntry_iterator_valid(&it); ext2_direntry_iterator_next(&it)) { - ext2_inode_t __inode; - if (ext2_read_inode(fs, &__inode, it.direntry->inode) == -1) { - pr_err("Failed to read the inode (%d).\n", it.direntry->inode); + if (it.direntry->inode != 0) { + ext2_inode_t __inode; + ext2_read_inode(fs, &__inode, it.direntry->inode); + pr_debug(" %-16s (inode: %4d, type: %2d, rec_len: %4d, name_len: %2d)[size: %d]\n", + it.direntry->name, + it.direntry->inode, + it.direntry->file_type, + it.direntry->rec_len, + it.direntry->name_len, + __inode.size); + } else { + pr_debug(" %-16s (inode: %4d, type: %2d, rec_len: %4d, name_len: %2d)\n", + it.direntry->name, + it.direntry->inode, + it.direntry->file_type, + it.direntry->rec_len, + it.direntry->name_len); } - pr_debug("name: %12s inode: %4d file_type: %2d rec_len: %d name_len: %d (realRL: %d, block: %d, total: %d)\n", - it.direntry->name, - it.direntry->inode, - it.direntry->file_type, - it.direntry->rec_len, - it.direntry->name_len, - ext2_get_rec_len_from_direntry(it.direntry), - it.block_offset, - it.total_offset); - ext2_dump_inode(&__inode); - pr_debug("\n"); + //ext2_dump_inode(&__inode); + //pr_debug("\n"); } + pr_debug("}\n"); kmem_cache_free(cache); } void ext2_test() { - vfs_file_t *home = ext2_open("/home", O_RDONLY, 0); - assert(home && "Failed to read home."); - - dump_dir(home); - - vfs_file_t *test1 = ext2_creat(home, "test1.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH); - assert(test1 && "Failed to create test1.txt."); - vfs_file_t *test2 = ext2_creat(home, "test2.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH); - assert(test2 && "Failed to create test2.txt."); - vfs_file_t *test3 = ext2_creat(home, "test3.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH); - assert(test3 && "Failed to create test3.txt."); - +#if 0 char buffer[256]; memset(buffer, 0, 256); for (int i = 0; i < 256; ++i) { @@ -3049,17 +3057,74 @@ void ext2_test() } buffer[255] = 0; + vfs_file_t *home, *test1, *test2, *test3; + + assert(home = vfs_open("/home", O_RDONLY, 0)); dump_dir(home); - ext2_write(test1, buffer, 0, 256); - ext2_write(test2, buffer, 0, 256); - ext2_write(test3, buffer, 0, 256); + assert(test1 = vfs_creat("/home/test1.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + dump_dir(home); + + assert(test2 = vfs_creat("/home/test2.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + dump_dir(home); + + assert(test3 = vfs_creat("/home/test3.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + dump_dir(home); + + vfs_write(test1, buffer, 0, 256); + vfs_close(test1); + dump_dir(home); + + vfs_write(test2, buffer, 0, 256); + vfs_close(test2); + dump_dir(home); + + vfs_write(test3, buffer, 0, 256); + vfs_close(test3); + dump_dir(home); + + vfs_mkdir("/home/pippo", EXT2_S_IRWXU | EXT2_S_IRGRP | EXT2_S_IXGRP | EXT2_S_IROTH | EXT2_S_IXOTH); + dump_dir(home); + + vfs_rmdir("/home/pippo"); + dump_dir(home); + + vfs_rmdir("/home"); + dump_dir(home); + + vfs_close(home); + + vfs_unlink("/home/test1.txt"); + dump_dir(home); + + vfs_unlink("/home/test2.txt"); + dump_dir(home); + + vfs_unlink("/home/test3.txt"); + dump_dir(home); +#elif 1 + vfs_file_t *home, *file; + assert(home = vfs_open("/home", O_RDONLY, 0)); dump_dir(home); - ext2_mkdir("/home/pippo", EXT2_S_IRWXU | EXT2_S_IRGRP | EXT2_S_IXGRP | EXT2_S_IROTH | EXT2_S_IXOTH); + assert(file = vfs_creat("/home/test_file_1.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + vfs_close(file); - ext2_rmdir("/home/pippo"); + assert(file = vfs_creat("/home/test_file_2.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + vfs_close(file); - ext2_rmdir("/home"); + vfs_unlink("/home/test_file_1.txt"); + vfs_unlink("/home/test_file_2.txt"); + + dump_dir(home); + + assert(file = vfs_creat("/home/test_file_4.txt", EXT2_S_IWUSR | EXT2_S_IRUSR | EXT2_S_IRGRP | EXT2_S_IROTH)); + vfs_close(file); + + dump_dir(home); + + vfs_close(home); + while (true) {} +#endif } \ No newline at end of file diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c index 8ffb0f9..a78618f 100644 --- a/mentos/src/fs/namei.c +++ b/mentos/src/fs/namei.c @@ -4,9 +4,10 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys/errno.h" -#include "fs/vfs.h" #include "process/scheduler.h" +#include "sys/errno.h" +#include "io/debug.h" +#include "fs/vfs.h" int sys_unlink(const char *path) { @@ -22,3 +23,42 @@ int sys_rmdir(const char *path) { return vfs_rmdir(path); } + +int sys_creat(const char *path, mode_t mode) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Search for an unused fd. + int fd; + for (fd = 0; fd < task->max_fd; ++fd) { + if (!task->fd_list[fd].file_struct) { + break; + } + } + + // Check if there is not fd available. + if (fd >= MAX_OPEN_FD) { + return -EMFILE; + } + + // If fd limit is reached, try to allocate more + if (fd == task->max_fd) { + if (!vfs_extend_task_fd_list(task)) { + pr_err("Failed to extend the file descriptor list.\n"); + return -EMFILE; + } + } + + // Try to open the file. + vfs_file_t *file = vfs_creat(path, mode); + if (file == NULL) { + return -errno; + } + + // Set the file descriptor id. + task->fd_list[fd].file_struct = file; + + // Return the file descriptor and increment it. + return fd; +} \ No newline at end of file diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 5243eba..86b9721 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -11,7 +11,7 @@ // Change the header. #define __DEBUG_HEADER__ "[VFS ]" // Set the log level. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG #include "process/scheduler.h" #include "klib/spinlock.h" @@ -144,22 +144,20 @@ vfs_file_t *vfs_open(const char *path, int flags, mode_t mode) vfs_file_t *file = sb_root->fs_operations->open_f(absolute_path, flags, mode); if (file == NULL) { pr_err("vfs_open(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); - errno = ENOENT; return NULL; } // Increment file reference counter. - ++file->count; + file->count += 1; // Return the file. return file; } int vfs_close(vfs_file_t *file) { - // Decrement file reference counter. - file->count--; - + pr_debug("vfs_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count - 1); + assert(file->count > 0); // Close file if it's the last reference. - if (file->count == 0) { + if (--file->count == 0) { // Check if the filesystem has the close function. if (file->fs_operations->close_f == NULL) { return -ENOSYS; @@ -296,6 +294,47 @@ int vfs_rmdir(const char *path) return sb_root->sys_operations->rmdir_f(absolute_path); } +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)) { + pr_err("vfs_creat(%s): Cannot get the absolute path.", path); + errno = ENODEV; + return NULL; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_creat(%s): Cannot find the superblock!\n"); + errno = ENODEV; + return NULL; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_creat(%s): Cannot find the superblock root.", 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); + errno = ENOSYS; + return NULL; + } + // Retrieve the file. + vfs_file_t *file = sb_root->sys_operations->creat_f(absolute_path, mode); + if (file == NULL) { + pr_err("vfs_open(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); + errno = ENOENT; + return NULL; + } + // Increment file reference counter. + file->count += 1; + // Return the file. + return file; +} + int vfs_stat(const char *path, stat_t *buf) { // Allocate a variable for the path. diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index ae59d2b..10d568d 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -350,6 +350,8 @@ int kmain(boot_info_t *boot_informations) } print_ok(); + //ext2_test(); + // We have completed the booting procedure. pr_notice("Booting done, jumping into init process.\n"); // Print the welcome message. diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index e9026dd..2c009ed 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -57,6 +57,7 @@ void syscall_init() sys_call_table[__NR_fstat] = (SystemCall)sys_fstat; sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; + sys_call_table[__NR_creat] = (SystemCall)sys_creat; sys_call_table[__NR_unlink] = (SystemCall)sys_unlink; sys_call_table[__NR_getdents] = (SystemCall)sys_getdents; sys_call_table[__NR_lseek] = (SystemCall)sys_lseek; diff --git a/programs/shell.c b/programs/shell.c index 5812244..bfbf46a 100644 --- a/programs/shell.c +++ b/programs/shell.c @@ -328,7 +328,7 @@ static int __cd(int argc, char *argv[]) } int fd = open(path, O_RDONLY | O_DIRECTORY, S_IXUSR); if (fd == -1) { - printf("cd: %s: %s\n\n", argv[0], strerror(errno), path); + printf("cd: %s\n\n", strerror(errno), path); return 1; } // Set current working directory.