Files
MentOS/mentos/src/fs/ext2.c
T
2021-12-14 01:51:48 +01:00

366 lines
14 KiB
C

/// @file ext2.c
/// @author Enrico Fraccaroli (enry.frak@gmail.com)
/// @brief EXT2 driver.
/// @version 0.1
/// @date 2021-12-13
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "fs/ext2.h"
#define __DEBUG_HEADER__ "[EXT2 ]"
#define __DEBUG_LEVEL__ 100
#include "process/scheduler.h"
#include "process/process.h"
#include "klib/spinlock.h"
#include "fs/vfs_types.h"
#include "sys/errno.h"
#include "io/debug.h"
#include "fs/vfs.h"
#include "string.h"
#include "stdio.h"
#include "fcntl.h"
#define EXT2_SUPERBLOCK_MAGIC 0xEF53
#define EXT2_INDIRECT_BLOCKS 12
#define EXT2_NAME_LEN 255
/// @brief The superblock contains all the information about the configuration
/// of the filesystem.
/// @details The primary copy of the superblock is stored at an offset of 1024
/// bytes from the start of the device, and it is essential to mounting the
/// filesystem. Since it is so important, backup copies of the superblock are
/// stored in block groups throughout the filesystem.
typedef struct ext2_superblock_t {
/// @brief Total number of inodes in file system.
uint32_t inodes_count;
/// @brief Total number of blocks in file system
uint32_t blocks_count;
/// @brief Number of blocks reserved for superuser.
uint32_t r_blocks_count;
/// @brief Total number of unallocated blocks.
uint32_t free_blocks_count;
/// @brief Total number of unallocated inodes.
uint32_t free_inodes_count;
/// @brief Block number of the block containing the superblock.
uint32_t first_data_block;
/// @brief The number to shift 1024 to the left by to obtain the block size
/// (log2 (block size) - 10).
uint32_t log_block_size;
/// @brief The number to shift 1,024 to the left by to obtain the fragment
/// size (log2 (fragment size) - 10).
uint32_t log_frag_size;
/// @brief Number of blocks in each block group.
uint32_t blocks_per_group;
/// @brief Number of fragments in each block group.
uint32_t frags_per_group;
/// @brief Number of inodes in each block group.
uint32_t inodes_per_group;
/// @brief Last mount time (in POSIX time).
uint32_t mtime;
/// @brief Last written time (in POSIX time).
uint32_t wtime;
/// @brief Number of times the volume has been mounted since its last
/// consistency check (fsck).
uint16_t mnt_count;
/// @brief Number of mounts allowed before a consistency check (fsck) must
/// be done.
uint16_t max_mnt_count;
/// @brief Ext2 signature (0xef53), used to help confirm the presence of
/// Ext2 on a volume.
uint16_t magic;
/// @brief File system state.
uint16_t state;
/// @brief What to do when an error is detected.
uint16_t errors;
/// @brief Minor portion of version (combine with Major portion below to
/// construct full version field).
uint16_t minor_rev_level;
/// @brief POSIX time of last consistency check (fsck).
uint32_t lastcheck;
/// @brief Interval (in POSIX time) between forced consistency checks
/// (fsck).
uint32_t checkinterval;
/// @brief Operating system ID from which the filesystem on this volume was
/// created.
uint32_t creator_os;
/// @brief Major portion of version (combine with Minor portion above to
/// construct full version field).
uint32_t rev_level;
/// @brief User ID that can use reserved blocks.
uint16_t def_resuid;
/// @brief Group ID that can use reserved blocks.
uint16_t def_resgid;
// == Extended Superblock Fields ==========================================
/// @brief First non-reserved inode in file system. (In versions < 1.0, this
/// is fixed as 11)
uint32_t first_ino;
/// @brief Size of each inode structure in bytes. (In versions < 1.0, this
/// is fixed as 128)
uint16_t inode_size;
/// @brief Block group that this superblock is part of (if backup copy).
uint16_t block_group_nr;
/// @brief Optional features present (features that are not required to read
/// or write, but usually result in a performance increase).
uint32_t feature_compat;
/// @brief Required features present (features that are required to be
/// supported to read or write)
uint32_t feature_incompat;
/// @brief Features that if not supported, the volume must be mounted
/// read-only).
uint32_t feature_ro_compat;
/// @brief File system ID (what is output by blkid).
uint8_t uuid[16];
/// @brief Volume name (C-style string: characters terminated by a 0 byte).
uint8_t volume_name[16];
/// @brief Path volume was last mounted to (C-style string: characters
/// terminated by a 0 byte).
uint8_t last_mounted[64];
/// @brief Compression algorithms used.
uint32_t algo_bitmap;
// == Performance Hints ===================================================
/// @brief Number of blocks to preallocate for files.
uint8_t prealloc_blocks;
/// @brief Number of blocks to preallocate for directories.
uint8_t prealloc_dir_blocks;
/// @brief (Unused)
uint16_t padding0;
// == Journaling Support ==================================================
/// @brief Journal ID
uint8_t journal_uuid[16];
/// @brief Inode number of journal file.
uint32_t journal_inum;
/// @brief Device number of journal file.
uint32_t jounral_dev;
/// @brief Start of list of inodes to delete.
uint32_t last_orphan;
// == Directory Indexing Support ==========================================
/// @brief HTree hash seed.
uint32_t hash_seed[4];
/// @brief Ddefault hash version to use.
uint8_t def_hash_version;
/// @brief Padding.
uint16_t padding1;
/// @brief Padding.
uint8_t padding2;
// == Other Options =======================================================
/// @brief The default mount options for the file system.
uint32_t default_mount_options;
/// @brief The ID of the first meta block group.
uint32_t first_meta_block_group_id;
/// @brief Reserved.
uint8_t reserved[760];
} ext2_superblock_t;
/// @brief
typedef struct ext2_group_descriptor_t {
/// @brief The block number of the block bitmap for this Block Group
uint32_t block_bitmap;
/// @brief The block number of the inode allocation bitmap for this Block Group.
uint32_t inode_bitmap;
/// @brief The block number of the starting block for the inode table for this Block Group.
uint32_t inode_table;
/// @brief Number of free blocks.
uint16_t free_blocks_count;
/// @brief Number of free inodes.
uint16_t free_inodes_count;
/// @brief Number of used directories.
uint16_t used_dirs_count;
/// @brief Padding.
uint16_t pad;
/// @brief Reserved.
uint32_t reserved[3];
} ext2_group_descriptor_t;
/// @brief The ext2 inode.
typedef struct ext2_inode_t {
/// @brief File mode
uint16_t mode;
/// @brief The user identifiers of the owners.
uint16_t uid;
/// @brief The size of the file in bytes.
uint32_t size;
/// @brief The time that the inode was accessed.
uint32_t atime;
/// @brief The time that the inode was created.
uint32_t ctime;
/// @brief The time that the inode was modified the last time.
uint32_t mtime;
/// @brief The time that the inode was deleted.
uint32_t dtime;
/// @brief The group identifiers of the owners.
uint16_t gid;
/// @brief Number of hard links.
uint16_t links_count;
/// @brief Blocks count.
uint32_t blocks_count;
/// @brief File flags.
uint32_t flags;
/// @brief OS dependant value.
uint32_t osd1;
union {
struct datablocks {
uint32_t dir_blocks[EXT2_INDIRECT_BLOCKS];
uint32_t indir_block;
uint32_t double_indir_block;
uint32_t triple_indir_block;
} blocks;
char symlink[60];
} data;
/// @brief Value used to indicate the file version (used by NFS).
uint32_t generation;
/// @brief Value indicating the block number containing the extended attributes.
uint32_t file_acl;
/// @brief For regular files this 32bit value contains the high 32 bits of the 64bit file size.
uint32_t dir_acl;
/// @brief Value indicating the location of the file fragment.
uint32_t fragment_addr;
/// @brief OS dependant structure.
uint32_t osd2[3];
} ext2_inode_t;
/// @brief The header of an ext2 directory entry.
typedef struct ext2_dirent_t {
/// @brief Inode number.
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.
char name[EXT2_NAME_LEN];
} ext2_dirent_t;
static const char *uuid_to_string(uint8_t uuid[16])
{
static char s[33] = { 0 };
sprintf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
return s;
}
static const char *time_to_string(uint32_t time)
{
static char s[250] = { 0 };
tm_t *tm = localtime(&time);
sprintf(s, "%2d/%2d %2d:%2d", tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
return s;
}
static void ext2_dump_superblock(ext2_superblock_t *sb)
{
pr_debug("inodes_count : %d\n", sb->inodes_count);
pr_debug("blocks_count : %d\n", sb->blocks_count);
pr_debug("r_blocks_count : %d\n", sb->r_blocks_count);
pr_debug("free_blocks_count : %d\n", sb->free_blocks_count);
pr_debug("free_inodes_count : %d\n", sb->free_inodes_count);
pr_debug("first_data_block : %d\n", sb->first_data_block);
pr_debug("log_block_size : %d\n", sb->log_block_size);
pr_debug("log_frag_size : %d\n", sb->log_frag_size);
pr_debug("blocks_per_group : %d\n", sb->blocks_per_group);
pr_debug("frags_per_group : %d\n", sb->frags_per_group);
pr_debug("inodes_per_group : %d\n", sb->inodes_per_group);
pr_debug("mtime : %s\n", time_to_string(sb->mtime));
pr_debug("wtime : %s\n", time_to_string(sb->wtime));
pr_debug("mnt_count : %d\n", sb->mnt_count);
pr_debug("max_mnt_count : %d\n", sb->max_mnt_count);
pr_debug("magic : 0x%0x\n", sb->magic);
pr_debug("state : %d\n", sb->state);
pr_debug("errors : %d\n", sb->errors);
pr_debug("minor_rev_level : %d\n", sb->minor_rev_level);
pr_debug("lastcheck : %s\n", time_to_string(sb->lastcheck));
pr_debug("checkinterval : %d\n", sb->checkinterval);
pr_debug("creator_os : %d\n", sb->creator_os);
pr_debug("rev_level : %d\n", sb->rev_level);
pr_debug("def_resuid : %d\n", sb->def_resuid);
pr_debug("def_resgid : %d\n", sb->def_resgid);
pr_debug("first_ino : %d\n", sb->first_ino);
pr_debug("inode_size : %d\n", sb->inode_size);
pr_debug("block_group_nr : %d\n", sb->block_group_nr);
pr_debug("feature_compat : %d\n", sb->feature_compat);
pr_debug("feature_incompat : %d\n", sb->feature_incompat);
pr_debug("feature_ro_compat : %d\n", sb->feature_ro_compat);
pr_debug("uuid : %s\n", uuid_to_string(sb->uuid));
pr_debug("volume_name : %s\n", (char *)sb->volume_name);
pr_debug("last_mounted : %s\n", (char *)sb->last_mounted);
pr_debug("algo_bitmap : %d\n", sb->algo_bitmap);
pr_debug("prealloc_blocks : %d\n", sb->prealloc_blocks);
pr_debug("prealloc_dir_blocks : %d\n", sb->prealloc_dir_blocks);
pr_debug("journal_uuid : %s\n", uuid_to_string(sb->journal_uuid));
pr_debug("journal_inum : %d\n", sb->journal_inum);
pr_debug("jounral_dev : %d\n", sb->jounral_dev);
pr_debug("last_orphan : %d\n", sb->last_orphan);
pr_debug("hash_seed : %u %u %u %u\n", sb->hash_seed[0], sb->hash_seed[1], sb->hash_seed[2], sb->hash_seed[3]);
pr_debug("def_hash_version : %d\n", sb->def_hash_version);
pr_debug("default_mount_options : %d\n", sb->default_mount_options);
pr_debug("first_meta_bg : %d\n", sb->first_meta_block_group_id);
}
static vfs_file_t *ext2_mount_callback(const char *path, const char *device)
{
vfs_file_t *dev = vfs_open(device, 0, 0);
if (dev && (dev->flags == DT_BLK)) {
pr_debug("Correctly found block device.\n");
ext2_superblock_t sb;
memset(&sb, 0, sizeof(ext2_superblock_t));
if (vfs_read(dev, &sb, 1024, sizeof(ext2_superblock_t)) == -1) {
pr_err("Failed to read the superblock table at 1024.\n");
return NULL;
}
if (sb.magic != EXT2_SUPERBLOCK_MAGIC) {
pr_err("Wrong magic number, it is not an EXT2 filesystem.\n");
return NULL;
}
ext2_dump_superblock(&sb);
}
return NULL;
}
/// 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",
.fs_flags = 0,
.mount = ext2_mount_callback
};
int ext2_initialize(void)
{
// Register the filesystem.
vfs_register_filesystem(&ext2_file_system_type);
return 0;
}
int ext2_finalize(void)
{
vfs_unregister_filesystem(&ext2_file_system_type);
return 0;
}