Add code to allocate new inodes. Simplify the code that searches for free inodes and blocks.
This commit is contained in:
+414
-114
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user