From 44e588b5860bfb819f5c8da6caf644404c524c37 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 23 Dec 2021 18:25:02 +0100 Subject: [PATCH] [WIP] File creation is working. Need some more testing. --- mentos/src/fs/ext2.c | 150 +++++++++++++++++++++++++++++++++++++------ mentos/src/kernel.c | 4 +- 2 files changed, 133 insertions(+), 21 deletions(-) diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index 176aa1e..776cb22 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -1604,10 +1604,104 @@ void ext2_direntry_iterator_next(ext2_direntry_iterator_t *iterator) // Directory Entry Management Functions // ============================================================================ -static int ext2_allocate_direntry(ext2_filesystem_t *fs, vfs_file_t *parent, char *name, uint32_t inode_index) +static inline uint32_t ext2_get_rec_len_from_name(const char *name) { - pr_err("Not implemented yet.\n"); - return -1; + unsigned int rec_len = sizeof(ext2_dirent_t) + strlen(name); + rec_len += (rec_len % 4) ? (4 - (rec_len % 4)) : 0; + return rec_len; +} + +static int ext2_allocate_direntry(ext2_filesystem_t *fs, vfs_file_t *parent, const char *name, uint32_t inode_index) +{ + // Get the inode associated with the file. + ext2_inode_t parent_inode; + if (ext2_read_inode(fs, &parent_inode, parent->ino) == -1) { + pr_err("Failed to read the inode (%d).\n", parent->ino); + return -1; + } + // Check that the parent is a directory. + if (!bitmask_check(parent_inode.mode, EXT2_S_IFDIR)) { + pr_err("The parent inode is not a directory (ino: %d, mode: %d).\n", parent->ino, parent_inode.mode); + return -1; + } + pr_debug("ext2_allocate_direntry(parent: \"%s\", name: \"%s\", inode: %d)\n", parent->name, name, inode_index); + // Compute the rec_len for the name of the new direntry. Remember, the name + // is not actually 256 chars long as specified in EXT2_NAME_LEN, that is + // just a maximum. + unsigned int rec_len = ext2_get_rec_len_from_name(name); + + pr_debug("Our directory entry looks like this:\n"); + pr_debug(" inode = %d\n", inode_index); + pr_debug(" rec_len = %d\n", rec_len); + pr_debug(" name = %s (%d)\n", name, strlen(name)); + pr_debug(" file_type = %d (vfs: %d)\n", EXT2_S_IFREG, DT_REG); + + // Allocate the cache. + uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL); + // Clean the cache. + memset(cache, 0, fs->block_size); + // Save the previous direntry. + ext2_dirent_t *previous = NULL; + // 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)) { + // Save the previous direntry. + previous = it.direntry; + // 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); + } else { + if ((it.block_offset + rec_len) >= fs->block_size) { + pr_debug("We need a new direntry, and a new block.\n"); + it.block_index += 1; + if (ext2_allocate_inode_block(fs, &parent_inode, parent->ino, it.block_index) == -1) { + pr_err("Failed to allocate a new block for an inode.\n"); + // Free the cache. + kmem_cache_free(cache); + return -1; + } + it.block_offset = 0; + parent_inode.size += fs->block_size; + if (ext2_write_inode(fs, &parent_inode, parent->ino) == -1) { + pr_err("Failed to update the inode of the father directory.\n"); + // Free the cache. + kmem_cache_free(cache); + return -1; + } + } else { + pr_debug("We need a new direntry, from the same block.\n"); + if (previous == NULL) { + pr_err("Previous direntry is NULL!\n"); + } else { + unsigned int sreclen = previous->name_len + sizeof(ext2_dirent_t); + sreclen += (sreclen % 4) ? (4 - (sreclen % 4)) : 0; + previous->rec_len = sreclen; + } + } + } + pr_debug(" total_offset = 0x%x\n", it.total_offset); + pr_debug(" block_offset = 0x%x\n", it.block_offset); + + ext2_dirent_t *new_direntry = (ext2_dirent_t *)((uintptr_t)cache + it.block_offset); + + new_direntry->inode = inode_index; + new_direntry->rec_len = fs->block_size - it.block_offset; + new_direntry->name_len = strlen(name); + new_direntry->file_type = ext2_file_type_regular_file; + memcpy(new_direntry->name, name, strlen(name)); + + 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; } /// @brief Finds the entry with the given `name` inside the `directory`. @@ -1878,34 +1972,34 @@ static vfs_file_t *ext2_find_vfs_file_with_inode(ext2_filesystem_t *fs, ino_t in /// @param fs the filesystem. /// @param inode the inode we use to initialize the root of the filesystem. /// @param preferred_group the preferred group where the inode should be allocated. -/// @return true on success, false on failure. -static bool_t ext2_create_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t preferred_group) +/// @return the inode index on success, -1 on failure. +static int ext2_create_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t preferred_group) { if (fs == NULL) { pr_err("Received a null EXT2 filesystem.\n"); - return false; + return -1; } if (inode == NULL) { pr_err("Received a null EXT2 inode.\n"); - return false; + return -1; } task_struct *task = scheduler_get_current_process(); if (task == NULL) { pr_err("Failed to get the current running process.\n"); - return false; + return -1; } // Allocate an inode, inside the preferred_group if possible. int inode_index = ext2_allocate_inode(fs, preferred_group); if (inode_index == 0) { pr_err("Failed to allocate a new inode.\n"); - return false; + return -1; } // Clean the inode structure. memset(inode, 0, sizeof(ext2_inode_t)); // Get the inode associated with the directory entry. if (ext2_read_inode(fs, inode, inode_index) == -1) { pr_err("Failed to read the newly created inode.\n"); - return false; + return -1; } // Set the user identifiers of the owners. inode->uid = task->uid; @@ -1944,9 +2038,9 @@ static bool_t ext2_create_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint // Write the inode. if (ext2_write_inode(fs, inode, inode_index) == -1) { pr_err("Failed to write the newly created inode.\n"); - return false; + return -1; } - return true; + return inode_index; } static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t mode) @@ -1957,21 +2051,31 @@ static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t mode) pr_err("The parent does not belong to an EXT2 filesystem `%s`.\n", parent->name); return NULL; } - pr_err("Not implemented yet.\n"); - -#if 0 // Get the group index of the parent. uint32_t group_index = ext2_get_group_index_from_inode(fs, parent->ino); // Create and initialize the new inode. ext2_inode_t inode; - if (!ext2_create_inode(fs, &inode, group_index)) { + int inode_index = ext2_create_inode(fs, &inode, 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; } // Initialize the file. - ext2_allocate_direntry(fs, parent, name, inode_index); -#endif - return NULL; + if (ext2_allocate_direntry(fs, parent, name, inode_index) == -1) { + pr_err("Failed to allocate a new direntry for the inode.\n"); + 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; + } + if (ext2_init_vfs_file(fs, new_file, &inode, inode_index, name, strlen(name)) == -1) { + pr_err("Failed to properly set the VFS file.\n"); + return NULL; + } + return new_file; } /// @brief Open the file at the given path and returns its file descriptor. @@ -2022,9 +2126,15 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) vfs_file_t *parent = ext2_open(parent_path, O_RDONLY, 0); if (parent) { // Create the file. - ext2_creat(parent, basename(path), mode); + 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; diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index b66db4d..df7a3df 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -350,7 +350,9 @@ int kmain(boot_info_t *boot_informations) } print_ok(); - vfs_file_t *file = vfs_open("/home/test.txt", O_CREAT | O_EXCL, 0); + vfs_file_t *file1 = vfs_open("/home/test4.txt", O_CREAT | O_EXCL, 0); + vfs_file_t *file2 = vfs_open("/home/test5.txt", O_CREAT | O_EXCL, 0); + vfs_file_t *file3 = vfs_open("/home/test6.txt", O_CREAT | O_EXCL, 0); while (true) {}