Add code to allocate new inodes. Simplify the code that searches for free inodes and blocks.

This commit is contained in:
Enrico Fraccaroli
2021-12-22 15:21:51 +01:00
parent 61e5eaa99a
commit 642284d3fb
+414 -114
View File
@@ -13,7 +13,7 @@
// Change the header.
#define __DEBUG_HEADER__ "[EXT2 ]"
// Set the log level.
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG
#include "process/scheduler.h"
#include "process/process.h"
@@ -22,6 +22,7 @@
#include "sys/errno.h"
#include "io/debug.h"
#include "fs/vfs.h"
#include "assert.h"
#include "libgen.h"
#include "string.h"
#include "stdio.h"
@@ -297,7 +298,7 @@ typedef struct ext2_dirent_t {
/// File type code.
uint8_t file_type;
/// File name.
char name[EXT2_NAME_LEN];
char name[];
} ext2_dirent_t;
/// @brief The details regarding the filesystem.
@@ -344,23 +345,25 @@ typedef struct ext2_filesystem_t {
spinlock_t spinlock;
} ext2_filesystem_t;
/// @brief Structure used when searching for a directory entry.
typedef struct ext2_direntry_search_t {
///
/// Pointer to the direntry where we store the search results.
ext2_dirent_t *direntry;
///
/// The inode of the parent directory.
ino_t parent_inode;
///
/// The index of the block where the direntry resides.
uint32_t block_index;
///
uint32_t dir_offset;
/// The offest of the direntry inside the block.
uint32_t block_offset;
} ext2_direntry_search_t;
// ============================================================================
// Forward Declaration of Functions
// ============================================================================
static bool_t ext2_check_block_bit(uint8_t *buffer, uint32_t index);
static void ext2_set_block_bit(uint8_t *buffer, uint32_t index, ext2_block_status_t status);
static ext2_block_status_t ext2_check_bitmap_bit(uint8_t *buffer, uint32_t index);
static void ext2_set_bitmap_bit(uint8_t *buffer, uint32_t index, ext2_block_status_t status);
static int ext2_read_superblock(ext2_filesystem_t *fs);
static int ext2_write_superblock(ext2_filesystem_t *fs);
static int ext2_read_block(ext2_filesystem_t *fs, uint32_t block_index, uint8_t *buffer);
@@ -571,7 +574,7 @@ static void ext2_dump_bgdt(ext2_filesystem_t *fs)
for (uint32_t j = 0; j < fs->block_size; ++j) {
if ((j % 8) == 0)
pr_debug(" Block index: %4d, Bitmap: %s\n", j / 8, dec_to_binary(cache[j / 8], 8));
if (!ext2_check_block_bit(cache, j)) {
if (!ext2_check_bitmap_bit(cache, j)) {
pr_debug(" First free block in group is in block %d, the linear index is %d\n", j / 8, j);
break;
}
@@ -582,7 +585,7 @@ static void ext2_dump_bgdt(ext2_filesystem_t *fs)
for (uint32_t j = 0; j < fs->block_size; ++j) {
if ((j % 8) == 0)
pr_debug(" Block index: %4d, Bitmap: %s\n", j / 8, dec_to_binary(cache[j / 8], 8));
if (!ext2_check_block_bit(cache, j)) {
if (!ext2_check_bitmap_bit(cache, j)) {
pr_debug(" First free block in group is in block %d, the linear index is %d\n", j / 8, j);
break;
}
@@ -613,6 +616,35 @@ static void ext2_dump_filesystem(ext2_filesystem_t *fs)
// EXT2 Core Functions
// ============================================================================
/// @brief Determining which block group contains an inode.
/// @param fs the ext2 filesystem structure.
/// @param inode_index the inode index.
/// @return the group index.
/// @details Remember that inode addressing starts from 1.
static uint32_t ext2_get_group_index_from_inode(ext2_filesystem_t *fs, uint32_t inode_index)
{
return (inode_index - 1) / fs->superblock.inodes_per_group;
}
/// @brief Determining the offest of the inode inside the block group.
/// @param fs the ext2 filesystem structure.
/// @param inode_index the inode index.
/// @return the offset of the inode inside the group.
/// @details Remember that inode addressing starts from 1.
static uint32_t ext2_get_inode_offest_in_group(ext2_filesystem_t *fs, uint32_t inode_index)
{
return (inode_index - 1) % fs->superblock.inodes_per_group;
}
/// @brief Determines which block contains our inode.
/// @param fs the ext2 filesystem structure.
/// @param inode_offset the inode offset inside the group.
/// @return which block contains our inode.
static uint32_t ext2_get_block_index_from_inode_offset(ext2_filesystem_t *fs, uint32_t inode_offset)
{
return (inode_offset * fs->superblock.inode_size) / fs->block_size;
}
/// @brief Cheks if the bit at the given index is free.
/// @param buffer the buffer containing the bitmap
/// @param index the index we want to check.
@@ -620,16 +652,16 @@ static void ext2_dump_filesystem(ext2_filesystem_t *fs)
/// @details
/// How we access the specific bits inside the bitmap takes inspiration from the
/// mailman's algorithm.
static bool_t ext2_check_block_bit(uint8_t *buffer, uint32_t index)
static ext2_block_status_t ext2_check_bitmap_bit(uint8_t *buffer, uint32_t index)
{
return bit_check(buffer[index / 8], index % 8) != 0;
return (ext2_block_status_t)(bit_check(buffer[index / 8], index % 8) != 0);
}
/// @brief Sets the bit at the given index accordingly to `status`.
/// @param buffer the buffer containing the bitmap
/// @param index the index we want to check.
/// @param status the new status of the block (free|occupied).
static void ext2_set_block_bit(uint8_t *buffer, uint32_t index, ext2_block_status_t status)
static void ext2_set_bitmap_bit(uint8_t *buffer, uint32_t index, ext2_block_status_t status)
{
if (status == ext2_block_status_occupied)
bit_set_assign(buffer[index / 8], index % 8);
@@ -637,6 +669,113 @@ static void ext2_set_block_bit(uint8_t *buffer, uint32_t index, ext2_block_statu
bit_clear_assign(buffer[index / 8], index % 8);
}
/// @brief Searches for a free inode inside the group data loaded inside the cache.
/// @param fs the ext2 filesystem structure.
/// @param cache the cache from which we read the bgdt data.
/// @param linear_index the output variable where we store the linear indes to the free inode.
/// @return true if we found a free inode, false otherwise.
static inline bool_t ext2_find_free_inode_in_group(
ext2_filesystem_t *fs,
uint8_t *cache,
uint32_t *linear_index,
bool_t skip_reserved)
{
for ((*linear_index) = 0; (*linear_index) < fs->superblock.inodes_per_group; ++(*linear_index)) {
// If we need to skip the reserved inodes, we skip the round if the
// index is that of a reserved inode (superblock.first_ino).
if (skip_reserved && ((*linear_index) < fs->superblock.first_ino))
continue;
// Check if the entry is free.
if (!ext2_check_bitmap_bit(cache, *linear_index))
return true;
}
return false;
}
/// @brief Searches for a free inode inside the Block Group Descriptor Table (BGDT).
/// @param fs the ext2 filesystem structure.
/// @param cache the cache from which we read the bgdt data.
/// @param group_index the output variable where we store the group index.
/// @param linear_index the output variable where we store the linear indes to the free inode.
/// @return true if we found a free inode, false otherwise.
static inline bool_t ext2_find_free_inode(
ext2_filesystem_t *fs,
uint8_t *cache,
uint32_t *group_index,
uint32_t *linear_index,
uint32_t preferred_group)
{
// If we received a preference, try to find a free inode in that specific group.
if (preferred_group != 0) {
// Set the group index to the preferred group.
(*group_index) = preferred_group;
// Find the first free inode. We need to ask to skip reserved inodes,
// only if we are in group 0.
if (ext2_find_free_inode_in_group(fs, cache, linear_index, (*group_index) == 0))
return true;
}
// Get the group and bit index of the first free block.
for ((*group_index) = 0; (*group_index) < fs->block_groups_count; ++(*group_index)) {
// Check if there are free inodes in this block group.
if (fs->block_groups[(*group_index)].free_inodes_count > 0) {
// Read the block bitmap.
if (ext2_read_block(fs, fs->block_groups[(*group_index)].inode_bitmap, cache) < 0) {
pr_err("Failed to read the inode bitmap for group `%d`.\n", (*group_index));
return false;
}
// Find the first free inode. We need to ask to skip reserved
// inodes, only if we are in group 0.
if (ext2_find_free_inode_in_group(fs, cache, linear_index, (*group_index) == 0))
return true;
}
}
return false;
}
/// @brief Searches for a free block inside the group data loaded inside the cache.
/// @param fs the ext2 filesystem structure.
/// @param cache the cache from which we read the bgdt data.
/// @param group_index the output variable where we store the group index.
/// @param linear_index the output variable where we store the linear indes to the free block.
/// @return true if we found a free block, false otherwise.
static inline bool_t ext2_find_free_block_in_group(ext2_filesystem_t *fs, uint8_t *cache, uint32_t *linear_index)
{
for ((*linear_index) = 0; (*linear_index) < fs->superblock.blocks_per_group; ++(*linear_index)) {
// Check if the entry is free.
if (!ext2_check_bitmap_bit(cache, *linear_index))
return true;
}
return false;
}
/// @brief Searches for a free block.
/// @param fs the ext2 filesystem structure.
/// @param cache the cache from which we read the bgdt data.
/// @param linear_index the output variable where we store the linear indes to the free block.
/// @return true if we found a free block, false otherwise.
static inline bool_t ext2_find_free_block(
ext2_filesystem_t *fs,
uint8_t *cache,
uint32_t *group_index,
uint32_t *linear_index)
{
// Get the group and bit index of the first free block.
for ((*group_index) = 0; (*group_index) < fs->block_groups_count; ++(*group_index)) {
// Check if there are free blocks in this block group.
if (fs->block_groups[(*group_index)].free_blocks_count > 0) {
// Read the block bitmap.
if (ext2_read_block(fs, fs->block_groups[(*group_index)].block_bitmap, cache) < 0) {
pr_err("Failed to read the block bitmap for group `%d`.\n", (*group_index));
return false;
}
// Find the first free block.
if (ext2_find_free_block_in_group(fs, cache, linear_index))
return true;
}
}
return false;
}
/// @brief Reads the superblock from the block device associated with this filesystem.
/// @param fs the ext2 filesystem structure.
/// @return the amount of data we read, or negative value for an error.
@@ -735,28 +874,25 @@ static int ext2_read_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t
return -1;
}
// Retrieve the group index.
uint32_t group_index = (inode_index - 1U) / fs->superblock.inodes_per_group;
uint32_t group_index = ext2_get_group_index_from_inode(fs, inode_index);
if (group_index > fs->block_groups_count) {
pr_err("Invalid group index computed from inode index `%d`.\n", inode_index);
return -1;
}
// Retrieve the group.
ext2_group_descriptor_t *group_desc = &fs->block_groups[group_index];
// Get the index of the inode inside the group.
uint32_t index = (inode_index - 1U) % fs->superblock.inodes_per_group;
uint32_t offset = ext2_get_inode_offest_in_group(fs, inode_index);
// Get the block offest.
uint32_t block = (index * fs->superblock.inode_size) / fs->block_size;
// Get the real inode index inside the block.
index %= fs->inodes_per_block_count;
uint32_t block = ext2_get_block_index_from_inode_offset(fs, offset);
// Get the real inode offset inside the block.
offset %= fs->inodes_per_block_count;
// Allocate the cache.
uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL);
// Clean the cache.
memset(cache, 0, fs->block_size);
// Read the block containing the inode table.
ext2_read_block(fs, group_desc->inode_table + block, cache);
ext2_read_block(fs, fs->block_groups[group_index].inode_table + block, cache);
// Save the inode content.
memcpy(inode, (ext2_inode_t *)((uintptr_t)cache + (index * fs->superblock.inode_size)), fs->superblock.inode_size);
memcpy(inode, (ext2_inode_t *)((uintptr_t)cache + (offset * fs->superblock.inode_size)), fs->superblock.inode_size);
// Free the cache.
kmem_cache_free(cache);
return 0;
@@ -774,83 +910,112 @@ static int ext2_write_inode(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t
return -1;
}
// Retrieve the group index.
uint32_t group_index = (inode_index - 1U) / fs->superblock.inodes_per_group;
uint32_t group_index = ext2_get_group_index_from_inode(fs, inode_index);
if (group_index > fs->block_groups_count) {
pr_err("Invalid group index computed from inode index `%d`.\n", inode_index);
return -1;
}
// Retrieve the group.
ext2_group_descriptor_t *group_desc = &fs->block_groups[group_index];
// Get the index of the inode inside the group.
uint32_t index = (inode_index - 1U) % fs->superblock.inodes_per_group;
// Get the offset of the inode inside the group.
uint32_t offset = ext2_get_inode_offest_in_group(fs, inode_index);
// Get the block offest.
uint32_t block = (index * fs->superblock.inode_size) / fs->block_size;
// Get the real inode index inside the block.
index %= fs->inodes_per_block_count;
uint32_t block = ext2_get_block_index_from_inode_offset(fs, offset);
// Get the real inode offset inside the block.
offset %= fs->inodes_per_block_count;
// Allocate the cache.
uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL);
// Clean the cache.
memset(cache, 0, fs->block_size);
// Read the block containing the inode table.
ext2_read_block(fs, group_desc->inode_table + block, cache);
ext2_read_block(fs, fs->block_groups[group_index].inode_table + block, cache);
// Write the inode.
memcpy((ext2_inode_t *)((uintptr_t)cache + (index * fs->superblock.inode_size)), inode, fs->superblock.inode_size);
memcpy((ext2_inode_t *)((uintptr_t)cache + (offset * fs->superblock.inode_size)), inode, fs->superblock.inode_size);
// Write back the block.
ext2_write_block(fs, group_desc->inode_table + block, cache);
ext2_write_block(fs, fs->block_groups[group_index].inode_table + block, cache);
// Free the cache.
kmem_cache_free(cache);
return 0;
}
/// @brief Allocate a new inode.
/// @param fs the filesystem.
/// @param preferred_group the preferred group.
/// @return index of the inode.
/// @details
/// Here are the rules used to allocate new inodes:
/// - the inode for a new file is allocated in the same group of the inode of
/// its parent directory.
/// - inodes are allocated equally between groups.
static int ext2_allocate_inode(ext2_filesystem_t *fs, unsigned preferred_group)
{
uint32_t group_index = 0, linear_index = 0, inode_index = 0;
// Lock the filesystem.
spinlock_lock(&fs->spinlock);
// Allocate the cache.
uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL);
// Clean the cache.
memset(cache, 0, fs->block_size);
// Search for a free inode.
if (!ext2_find_free_inode(fs, cache, &group_index, &linear_index, preferred_group)) {
pr_warning("Failed to find a free inode.\n");
// Unlock the filesystem.
spinlock_unlock(&fs->spinlock);
// Free the cache.
kmem_cache_free(cache);
return 0;
}
// Compute the inode index.
inode_index = (group_index * fs->superblock.inodes_per_group) + linear_index + 1U;
// Set the inode as occupied.
ext2_set_bitmap_bit(cache, linear_index, ext2_block_status_occupied);
// Write back the inode bitmap.
ext2_write_block(fs, fs->block_groups[group_index].inode_bitmap, cache);
// Free the cache.
kmem_cache_free(cache);
// Reduce the number of free inodes.
fs->block_groups[group_index].free_inodes_count -= 1;
// Update the bgdt.
ext2_write_bgdt(fs);
// Reduce the number of inodes inside the superblock.
fs->superblock.free_inodes_count -= 1;
// Update the superblock.
ext2_write_superblock(fs);
// Unlock the filesystem.
spinlock_unlock(&fs->spinlock);
// Return the inode.
return inode_index;
}
/// @brief Allocates a new block.
/// @param fs the filesystem.
/// @return 0 on failure, or the index of the new block on success.
static uint32_t ext2_allocate_block(ext2_filesystem_t *fs)
{
uint32_t group_index, block_index, linear_index;
uint32_t group_index = 0, linear_index = 0, block_index = 0;
// Lock the filesystem.
spinlock_lock(&fs->spinlock);
// Allocate the cache.
uint8_t *cache = kmem_cache_alloc(fs->ext2_buffer_cache, GFP_KERNEL);
// Clean the cache.
memset(cache, 0, fs->block_size);
// Get the group and bit index of the first free block.
for (group_index = 0; group_index < fs->block_groups_count; ++group_index) {
// Check if there are free blocks in this block group.
if (fs->block_groups[group_index].free_blocks_count > 0) {
// Read the block bitmap.
ext2_read_block(fs, fs->block_groups[group_index].block_bitmap, cache);
// Find the first free block.
for (linear_index = 0; linear_index < fs->block_size; ++linear_index) {
// We found a free block.
if (!ext2_check_block_bit(cache, linear_index)) {
// Compute the block index.
block_index = (fs->superblock.blocks_per_group * group_index) + linear_index;
break;
}
}
if (block_index != 0)
break;
}
}
// Check if we have found a free block.
if (block_index == 0) {
pr_err("Cannot find a free block.\n");
goto free_cache_return_error;
// Search for a free block.
if (!ext2_find_free_block(fs, cache, &group_index, &linear_index)) {
pr_warning("Failed to find a free block.\n");
// Unlock the filesystem.
spinlock_unlock(&fs->spinlock);
// Free the cache.
kmem_cache_free(cache);
return 0;
}
// Compute the block index.
block_index = (group_index * fs->superblock.blocks_per_group) + linear_index;
// Set the block as occupied.
ext2_set_block_bit(cache, linear_index, ext2_block_status_occupied);
ext2_set_bitmap_bit(cache, linear_index, ext2_block_status_occupied);
// Update the bitmap.
ext2_write_block(fs, fs->block_groups[group_index].block_bitmap, cache);
// Decrease the number of free blocks inside the BGDT entry.
fs->block_groups[group_index].free_blocks_count -= 1;
// Update the BGDT.
if (ext2_write_bgdt(fs) == -1) {
pr_err("Cannot allocate the block.\n");
goto free_cache_return_error;
}
ext2_write_bgdt(fs);
// Decrease the number of free blocks inside the superblock.
fs->superblock.free_blocks_count -= 1;
// Update the superblock.
@@ -863,12 +1028,6 @@ static uint32_t ext2_allocate_block(ext2_filesystem_t *fs)
// Unlock the spinlock.
spinlock_unlock(&fs->spinlock);
return block_index;
free_cache_return_error:
// Free the cache.
kmem_cache_free(cache);
// Unlock the spinlock.
spinlock_unlock(&fs->spinlock);
return 0;
}
/// @brief Sets the real block index based on the block index inside an inode.
@@ -1351,16 +1510,18 @@ free_cache_return_error:
return -1;
}
static int ext2_allocate_direntry(ext2_filesystem_t *fs, vfs_file_t *parent, char *name, uint32_t inode_index)
{
pr_err("Not implemented yet.\n");
return -1;
}
/// @brief Finds the entry with the given `name` inside the `directory`.
/// @param directory the directory in which we perform the search.
/// @param name the name of the entry we are looking for.
/// @param search the output variable where we save the info about the entry.
/// @return 0 on success, -1 on failure.
static int ext2_find_entry(
ext2_filesystem_t *fs,
ino_t ino,
const char *name,
ext2_direntry_search_t *search)
static int ext2_find_entry(ext2_filesystem_t *fs, ino_t ino, const char *name, ext2_direntry_search_t *search)
{
if (fs == NULL) {
pr_err("You provided a NULL filesystem.\n");
@@ -1388,7 +1549,7 @@ static int ext2_find_entry(
pr_err("Failed to read the inode (%d).\n", ino);
goto free_cache_return_error;
}
uint32_t block_index = 0, dir_offset = 0, total_offset = 0;
uint32_t block_index = 0, block_offset = 0, total_offset = 0;
ext2_dirent_t *entry = NULL;
// Start by reading the first block of the inode.
if (!ext2_read_inode_block(fs, &inode, block_index, cache)) {
@@ -1398,11 +1559,11 @@ static int ext2_find_entry(
// Keep reading until we searched the whole inode.
while (total_offset < inode.size) {
// If we exceed the size of a block, move to the next block.
if (dir_offset >= fs->block_size) {
if (block_offset >= fs->block_size) {
// Increase the block index.
++block_index;
// Remove the exceeding size, so that we start correctly in the new block.
dir_offset -= fs->block_size;
block_offset -= fs->block_size;
// Read the new block.
if (!ext2_read_inode_block(fs, &inode, block_index, cache)) {
pr_err("Failed to read the inode block `%d`\n", block_index);
@@ -1410,7 +1571,7 @@ static int ext2_find_entry(
}
}
// Get the directory entry.
entry = (ext2_dirent_t *)((uintptr_t)cache + dir_offset);
entry = (ext2_dirent_t *)((uintptr_t)cache + block_offset);
if (!strcmp(entry->name, ".") && !strcmp(name, "/")) {
break;
}
@@ -1419,11 +1580,14 @@ static int ext2_find_entry(
if (!strncmp(entry->name, name, entry->name_len))
break;
// Advance the offsets.
dir_offset += entry->rec_len;
block_offset += entry->rec_len;
total_offset += entry->rec_len;
// Reset the direntry pointer.
entry = NULL;
}
// 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 (entry == NULL)
goto free_cache_return_error;
// Copy the direntry.
@@ -1431,9 +1595,7 @@ static int ext2_find_entry(
// Copy the index of the block containing the direntry.
search->block_index = block_index;
// Copy the offset of the direntry inside the block.
search->dir_offset = dir_offset;
// Copy the inode of the parent.
search->parent_inode = ino;
search->block_offset = block_offset;
// Free the cache.
kmem_cache_free(cache);
return 0;
@@ -1516,9 +1678,10 @@ static int ext2_resolve_path_direntry(vfs_file_t *directory, char *path, ext2_di
ext2_direntry_search_t search;
memset(&search, 0, sizeof(ext2_direntry_search_t));
// Initialize the search structure.
search.direntry = direntry;
search.block_index = 0;
search.dir_offset = 0;
search.direntry = direntry;
search.block_index = 0;
search.block_offset = 0;
search.parent_inode = 0;
return ext2_resolve_path(directory, path, &search);
}
@@ -1527,7 +1690,7 @@ static int ext2_resolve_path_direntry(vfs_file_t *directory, char *path, ext2_di
/// @return a pointer to the EXT2 filesystem, NULL otherwise.
static ext2_filesystem_t *get_ext2_filesystem(const char *absolute_path)
{
pr_debug("get_ext2_filesystem(%s)\n", path);
pr_debug("get_ext2_filesystem(%s)\n", absolute_path);
if (absolute_path == NULL) {
pr_err("We received a NULL absolute path.\n");
return NULL;
@@ -1655,10 +1818,100 @@ static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_diren
return 0;
}
static vfs_file_t *ext2_find_vfs_file_with_inode(ext2_filesystem_t *fs, ino_t inode)
{
vfs_file_t *file = NULL;
if (!list_head_empty(&fs->opened_files)) {
list_for_each_decl(it, &fs->opened_files)
{
// Get the file structure.
file = list_entry(it, vfs_file_t, siblings);
if (file && (file->ino == inode))
return file;
}
}
return NULL;
}
// ============================================================================
// Virtual FileSystem (VFS) Functions
// ============================================================================
static vfs_file_t *ext2_creat(vfs_file_t *parent, const char *name, mode_t mode)
{
pr_err("Not implemented yet.\n");
#if 0
// 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;
}
task_struct *task = scheduler_get_current_process();
if (task == NULL) {
pr_err("Failed to get the current running process.\n");
return NULL;
}
// Allocate an inode for it (TODO: Use the parent block hint).
ino_t inode_index = ext2_allocate_inode(fs, 0);
if (inode_index == 0) {
pr_err("Failed to allocate a new inode for `%s`.\n", name);
return NULL;
}
// 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, inode_index) == -1) {
pr_err("Failed to read the newly created inode for `%s`.\n", name);
return NULL;
}
// Set the file mode.
inode.mode = EXT2_S_IFREG | (0xFFF & mode);
// Set the user identifiers of the owners.
inode.uid = task->uid;
// Set the size of the file in bytes.
inode.size = 0;
// Set the time that the inode was accessed.
inode.atime = sys_time(NULL);
// Set the time that the inode was created.
inode.ctime = inode.atime;
// Set the time that the inode was modified the last time.
inode.mtime = inode.atime;
// Set the time that the inode was deleted.
inode.dtime = 0;
// Set the group identifiers of the owners.
inode.gid = task->gid;
// Set the number of hard links.
inode.links_count = 1;
// Set the blocks count.
inode.blocks_count = 0;
// Set the file flags.
inode.flags = 0;
// Set the OS dependant value.
inode.osd1 = 0;
// Set the blocks data.
memset(&inode.data, 0, sizeof(inode.data));
// Set the value used to indicate the file version (used by NFS).
inode.generation = 0;
// TODO: The value indicating the block number containing the extended attributes.
inode.file_acl = 0;
// TODO: For regular files this 32bit value contains the high 32 bits of the 64bit file size.
inode.dir_acl = 0;
// TODO:Value indicating the location of the file fragment.
inode.fragment_addr = 0;
// TODO: OS dependant structure.
memset(&inode.osd2, 0, sizeof(inode.osd2));
// Write the inode.
ext2_write_inode(fs, &inode, inode_index);
// Initialize the file.
ext2_allocate_direntry(fs, parent, name, inode_index);
#endif
return NULL;
}
/// @brief Open the file at the given path and returns its file descriptor.
/// @param path The path to the file.
/// @param flags The flags used to determine the behavior of the function.
@@ -1683,29 +1936,76 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode)
// Prepare the structure for the direntry.
ext2_dirent_t direntry;
memset(&direntry, 0, sizeof(ext2_dirent_t));
// Resolve the path.
if (ext2_resolve_path_direntry(fs->root, absolute_path, &direntry) == -1) {
pr_err("Failed to resolve absolute path `%s`.\n", absolute_path);
return NULL;
// Prepare the structure for the search.
ext2_direntry_search_t search;
memset(&search, 0, sizeof(ext2_direntry_search_t));
// Initialize the search structure.
search.direntry = &direntry;
search.block_index = 0;
search.block_offset = 0;
search.parent_inode = 0;
// 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);
return NULL;
}
} else {
// If we need to create it, it's ok if it does not exist.
if (bitmask_check(flags, O_CREAT)) {
pr_warning("We want to create a new file at `%s`.\n", path);
pr_warning("The inode of the parent is `%d`.\n", search.parent_inode);
#if 0
// Prepare the structure for the inode.
ext2_inode_t parent_inode;
memset(&parent_inode, 0, sizeof(ext2_inode_t));
// Get the inode associated with the directory entry.
if (ext2_read_inode(fs, &parent_inode, search.parent_inode) == -1) {
pr_err("Failed to read the inode of the parent `%d`.\n", search.parent_inode);
return NULL;
}
// Search for the parent VFS file.
vfs_file_t *parent = ext2_find_vfs_file_with_inode(fs, search.parent_inode);
if (parent == NULL) {
// Allocate the memory for the parent file.
parent = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL);
if (parent == NULL) {
pr_err("Failed to allocate memory for the parent.\n");
return NULL;
}
if (ext2_init_file(fs, &parent_inode, &direntry, parent) == -1) {
pr_err("Failed to properly set the VFS file.\n");
kmem_cache_free(parent);
return NULL;
}
// Add the vfs_file to the list of associated files.
list_head_add_tail(&parent->siblings, &fs->opened_files);
// Create the file.
vfs_file_t *new_file = ext2_creat(parent, basename(path), mode);
// Remove the parent from the list of opened VFS files.
list_head_del(&parent->siblings);
// Destroy the VFS file.
kmem_cache_free(parent);
return new_file;
} else {
return ext2_creat(parent, basename(path), mode);
}
#endif
return NULL;
} else {
pr_err("Failed to resolve absolute path `%s`.\n", absolute_path);
return NULL;
}
}
// Get the inode associated with the directory entry.
// 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, direntry.inode) == -1) {
pr_err("Failed to read the inode of `%s`.\n", direntry.name);
return NULL;
}
vfs_file_t *file = NULL;
if (!list_head_empty(&fs->opened_files)) {
list_for_each_decl(it, &fs->opened_files)
{
// Get the file structure.
vfs_file_t *opened_file = list_entry(it, vfs_file_t, siblings);
if (opened_file && (opened_file->ino == direntry.inode)) {
file = opened_file;
break;
}
}
}
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);
@@ -1754,7 +2054,7 @@ static int ext2_unlink(const char *path)
// Initialize the search structure.
search.direntry = &direntry;
search.block_index = 0;
search.dir_offset = 0;
search.block_offset = 0;
search.parent_inode = 0;
// Resolve the path to the directory entry.
if (ext2_resolve_path(fs->root, absolute_path, &search)) {
@@ -1776,7 +2076,7 @@ static int ext2_unlink(const char *path)
goto free_cache_return_error;
}
// Get a pointer to the direntry.
ext2_dirent_t *actual_dirent = (ext2_dirent_t *)((uintptr_t)cache + search.dir_offset);
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;
@@ -1788,7 +2088,7 @@ static int ext2_unlink(const char *path)
pr_err("Failed to write the inode block `%d`\n", search.block_index);
goto free_cache_return_error;
}
// Read the inode of the actual direntry.
// Read the inode of the direntry we want to unlink.
ext2_inode_t inode;
if (ext2_read_inode(fs, &inode, direntry.inode) == -1) {
pr_err("Failed to read the inode of `%s`.\n", direntry.name);
@@ -1998,7 +2298,7 @@ static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t co
return -ENOENT;
}
uint32_t block_index = 0, dir_offset = 0, total_offset = 0, written = 0;
uint32_t block_index = 0, block_offset = 0, total_offset = 0, written = 0;
off_t current = 0;
ext2_dirent_t *direntry = NULL;
// Allocate the cache.
@@ -2015,11 +2315,11 @@ static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t co
// Keep reading until we searched the whole inode.
while ((total_offset < inode.size) && (written < count)) {
// If we exceed the size of a block, move to the next block.
if (dir_offset >= fs->block_size) {
if (block_offset >= fs->block_size) {
// Increase the block index.
++block_index;
// Remove the exceeding size, so that we start correctly in the new block.
dir_offset -= fs->block_size;
block_offset -= fs->block_size;
// Read the new block.
if (!ext2_read_inode_block(fs, &inode, block_index, cache)) {
pr_err("Failed to read the inode block `%d`\n", block_index);
@@ -2027,14 +2327,14 @@ static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t co
}
}
// Get the directory entry.
direntry = (ext2_dirent_t *)((uintptr_t)cache + dir_offset);
direntry = (ext2_dirent_t *)((uintptr_t)cache + block_offset);
if (direntry == NULL) {
pr_err("We found a NULL ext2_dirent_t\n");
goto free_cache_return_error;
}
// Advance the offsets.
dir_offset += direntry->rec_len;
block_offset += direntry->rec_len;
total_offset += direntry->rec_len;
// Skip if already provided.