Add direntry search and new file generation.

This commit is contained in:
Enrico Fraccaroli
2021-12-16 01:51:31 +01:00
parent 73ef08dc11
commit fe13f087c9
+262 -33
View File
@@ -268,15 +268,15 @@ typedef struct ext2_inode_t {
/// @brief The header of an ext2 directory entry.
typedef struct ext2_dirent_t {
/// @brief Inode number.
/// Number of the inode that this directory entry points to.
uint32_t inode;
/// @brief Directory entry length
uint16_t direntlen;
/// @brief Name length
uint8_t namelen;
/// @brief File type.
uint8_t filetype;
/// @brief File name.
/// Length of this directory entry. Must be a multiple of 4.
uint16_t rec_len;
/// Length of the file name.
uint8_t name_len;
/// File type code.
uint8_t file_type;
/// File name.
char name[EXT2_NAME_LEN];
} ext2_dirent_t;
@@ -958,7 +958,7 @@ static int ext2_allocate_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode,
/// @param inode the inode which we are working with.
/// @param block_index the index of the block within the inode.
/// @param buffer the buffer where to put the data.
/// @return the amount read.
/// @return the amount of data we read, or negative value for an error.
static ssize_t ext2_read_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, unsigned int block_index, char *buffer)
{
if (block_index >= (inode->blocks_count / fs->blocks_per_block_count)) {
@@ -974,6 +974,177 @@ static ssize_t ext2_read_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode,
return ext2_read_block(fs, real_index, buffer);
}
/// @brief Writes the real block starting from an inode and the block index inside the inode.
/// @param fs the filesystem.
/// @param inode the inode which we are working with.
/// @param inode_index The index of the inode.
/// @param block_index the index of the block within the inode.
/// @param buffer the buffer where to put the data.
/// @return the amount of data we wrote, or negative value for an error.
static ssize_t ext2_write_inode_block(ext2_filesystem_t *fs, ext2_inode_t *inode, unsigned int inode_index, unsigned int block_index, char *buffer)
{
while (block_index >= (inode->blocks_count / fs->blocks_per_block_count)) {
pr_err("Tried to read an invalid block `%d`, but inode only has %d!",
block_index, (inode->blocks_count / fs->blocks_per_block_count));
ext2_allocate_inode_block(fs, inode, inode_index, (inode->blocks_count / fs->blocks_per_block_count));
ext2_write_inode(fs, inode, inode_index);
}
// Get the real index.
unsigned int real_index = ext2_get_real_block_index(fs, inode, block_index);
if (real_index == 0)
return -1;
// Write the block.
return ext2_write_block(fs, real_index, buffer);
}
static ssize_t ext2_write_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, uint32_t inode_index, off_t offset, size_t nbyte, char *buffer)
{
uint32_t end = offset + nbyte;
if (end > inode->size) {
inode->size = end;
}
uint32_t start_block = offset / fs->block_size;
uint32_t end_block = end / fs->block_size;
uint32_t end_size = end - end_block * fs->block_size;
uint32_t size_to_read = end - offset;
if (start_block == end_block) {
if (!ext2_read_inode_block(fs, inode, start_block, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", start_block);
return -1;
}
memcpy((uint8_t *)(((uintptr_t)fs->block_buffer) + ((uintptr_t)offset % fs->block_size)), buffer, size_to_read);
if (!ext2_write_inode_block(fs, inode, inode_index, start_block, fs->block_buffer)) {
pr_err("Failed to write the inode block `%d`\n", start_block);
return -1;
}
} else {
uint32_t block_offset, blocks_read = 0;
for (block_offset = start_block; block_offset < end_block; ++block_offset, ++blocks_read) {
if (block_offset == start_block) {
if (!ext2_read_inode_block(fs, inode, block_offset, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", block_offset);
return -1;
}
memcpy((uint8_t *)(((uintptr_t)fs->block_buffer) + ((uintptr_t)offset % fs->block_size)), buffer, fs->block_size - (offset % fs->block_size));
if (!ext2_write_inode_block(fs, inode, inode_index, block_offset, fs->block_buffer)) {
pr_err("Failed to write the inode block `%d`\n", start_block);
return -1;
}
//if (!b) {
// refresh_inode(fs, inode, inode_index);
//}
} else {
if (!ext2_read_inode_block(fs, inode, block_offset, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", block_offset);
return -1;
}
memcpy(fs->block_buffer, buffer + fs->block_size * blocks_read - (offset % fs->block_size), fs->block_size);
if (!ext2_write_inode_block(fs, inode, inode_index, block_offset, fs->block_buffer)) {
pr_err("Failed to write the inode block `%d`\n", start_block);
return -1;
}
//if (!b) {
// refresh_inode(fs, inode, inode_index);
//}
}
}
if (end_size) {
if (!ext2_read_inode_block(fs, inode, end_block, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", block_offset);
return -1;
}
memcpy(fs->block_buffer, buffer + fs->block_size * blocks_read - (offset % fs->block_size), end_size);
if (ext2_write_inode_block(fs, inode, inode_index, end_block, fs->block_buffer)) {
pr_err("Failed to write the inode block `%d`\n", start_block);
return -1;
}
}
}
return size_to_read;
}
static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_dirent_t *direntry, vfs_file_t *file);
static vfs_file_t *ext2_find_entry(vfs_file_t *file, char *name)
{
// Get the filesystem.
ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device;
if (fs == NULL) {
pr_err("The file does not belong to an EXT2 filesystem (%s).\n", file->name);
return NULL;
}
// Get the inode associated with the file.
ext2_inode_t inode;
if (ext2_read_inode(fs, &inode, file->ino) == -1) {
pr_err("Failed to read the inode (%d).\n", file->ino);
return NULL;
}
uint32_t block_index = 0, dir_offset = 0, total_offset = 0;
ext2_dirent_t *direntry = NULL;
// Start by reading the first block of the inode.
if (!ext2_read_inode_block(fs, &inode, block_index, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", block_index);
return NULL;
}
// 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) {
// Increase the block index.
++block_index;
// Remove the exceeding size, so that we start correctly in the new block.
dir_offset -= fs->block_size;
// Read the new block.
if (!ext2_read_inode_block(fs, &inode, block_index, fs->block_buffer)) {
pr_err("Failed to read the inode block `%d`\n", block_index);
return NULL;
}
}
// Get the directory entry.
direntry = (ext2_dirent_t *)((uintptr_t)fs->block_buffer + dir_offset);
pr_debug("direntry : `%s` (%d vs %d)\n", direntry->name, direntry->name_len, strlen(name));
// Check if the entry has the same name.
if ((direntry->inode != 0) && (strlen(name) == direntry->name_len))
if (!strcmp(direntry->name, name))
break;
// Advance the offsets.
dir_offset += direntry->rec_len;
total_offset += direntry->rec_len;
// Reset the direntry pointer.
direntry = NULL;
}
if (!direntry) {
return NULL;
}
vfs_file_t *new_file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL);
memset(new_file, 0, sizeof(vfs_file_t));
if (ext2_read_inode(fs, &inode, direntry->inode) == -1) {
pr_err("Failed to read the inode (%d).\n", direntry->inode);
kmem_cache_free(new_file);
return NULL;
}
if (ext2_init_file(fs, &inode, direntry, new_file) == -1) {
pr_err("Failed to initialize the new file (%d).\n", direntry->inode);
kmem_cache_free(new_file);
return NULL;
}
return new_file;
}
static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode)
{
return NULL;
}
static int ext2_close(vfs_file_t *file)
{
return 0;
}
static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nbyte)
{
// Get the filesystem.
@@ -1028,6 +1199,86 @@ static ssize_t ext2_read(vfs_file_t *file, char *buffer, off_t offset, size_t nb
return size_to_read;
}
static ssize_t ext2_write(vfs_file_t *file, const void *buffer, off_t offset, size_t nbyte)
{
// Get the filesystem.
ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device;
if (fs == NULL) {
pr_err("The file does not belong to an EXT2 filesystem (%s).\n", file->name);
return -1;
}
// Get the inode associated with the file.
ext2_inode_t inode;
if (ext2_read_inode(fs, &inode, file->ino) == -1) {
pr_err("Failed to read the inode (%s).\n", file->name);
return -1;
}
return ext2_write_inode_data(fs, &inode, file->ino, offset, nbyte, (char *)buffer);
}
/// Filesystem general operations.
static vfs_sys_operations_t ext2_sys_operations = {
.mkdir_f = NULL,
.rmdir_f = NULL,
.stat_f = NULL
};
/// Filesystem file operations.
static vfs_file_operations_t ext2_fs_operations = {
.open_f = ext2_open,
.unlink_f = NULL,
.close_f = ext2_close,
.read_f = ext2_read,
.write_f = ext2_write,
.lseek_f = NULL,
.stat_f = NULL,
.ioctl_f = NULL,
.getdents_f = NULL
};
static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_dirent_t *direntry, vfs_file_t *file)
{
// Information for root dir.
file->device = (void *)fs;
file->ino = direntry->inode;
memcpy(&file->name, &direntry->name, direntry->name_len);
file->name[direntry->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.
file->flags = 0;
if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) {
file->flags |= DT_REG;
}
if ((inode->mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) {
file->flags |= DT_DIR;
}
if ((inode->mode & EXT2_S_IFBLK) == EXT2_S_IFBLK) {
file->flags |= DT_BLK;
}
if ((inode->mode & EXT2_S_IFCHR) == EXT2_S_IFCHR) {
file->flags |= DT_CHR;
}
if ((inode->mode & EXT2_S_IFIFO) == EXT2_S_IFIFO) {
file->flags |= DT_FIFO;
}
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;
file->sys_operations = &ext2_sys_operations;
file->fs_operations = &ext2_fs_operations;
list_head_init(&file->siblings);
return 0;
}
static int ext2_set_root(ext2_filesystem_t *fs, ext2_inode_t *inode)
{
// Information for root dir.
@@ -1045,14 +1296,12 @@ static int ext2_set_root(ext2_filesystem_t *fs, ext2_inode_t *inode)
fs->root->flags = 0;
if ((inode->mode & EXT2_S_IFREG) == EXT2_S_IFREG) {
pr_err("The root inode is a regular file.\n");
kmem_cache_free(fs->root);
return -1;
}
if ((inode->mode & EXT2_S_IFDIR) == EXT2_S_IFDIR) {
fs->root->flags |= DT_DIR;
} else {
pr_err("The root inode is not a directory.\n");
kmem_cache_free(fs->root);
return -1;
}
if ((inode->mode & EXT2_S_IFBLK) == EXT2_S_IFBLK) {
@@ -1070,8 +1319,8 @@ static int ext2_set_root(ext2_filesystem_t *fs, ext2_inode_t *inode)
fs->root->atime = inode->atime;
fs->root->mtime = inode->mtime;
fs->root->ctime = inode->ctime;
fs->root->sys_operations = NULL;
fs->root->fs_operations = NULL;
fs->root->sys_operations = &ext2_sys_operations;
fs->root->fs_operations = &ext2_fs_operations;
list_head_init(&fs->root->siblings);
return 0;
}
@@ -1241,26 +1490,6 @@ static vfs_file_t *ext2_mount_callback(const char *path, const char *device)
return ext2_mount(block_device);
}
/// Filesystem general operations.
static vfs_sys_operations_t ext2_sys_operations = {
.mkdir_f = NULL,
.rmdir_f = NULL,
.stat_f = NULL
};
/// Filesystem file operations.
static vfs_file_operations_t ext2_fs_operations = {
.open_f = NULL,
.unlink_f = NULL,
.close_f = NULL,
.read_f = NULL,
.write_f = NULL,
.lseek_f = NULL,
.stat_f = NULL,
.ioctl_f = NULL,
.getdents_f = NULL
};
/// Filesystem information.
static file_system_type ext2_file_system_type = {
.name = "ext2",